libzahl

big integer library
git clone git://git.suckless.org/libzahl
Log | Files | Refs | README | LICENSE

commit 5b10b24044b3350a19ab3d3c0b37b5e9c12365b1
parent f6cb7f3e7382a19a6d6d9990c243ffb8a666182d
Author: Mattias Andrée <maandree@kth.se>
Date:   Sun, 13 Mar 2016 23:54:56 +0100

Multiple changes:

1)  Compile test with -O0, it takes too long otherwise.

2)  Add error codes: ZERROR_0_POW_0, ZERROR_0_DIV_0, ZERROR_DIV_0, ZERROR_NEGATIVE.

3)  Add workaround for a bug in clang (src/allocator.c).

4)  Cleanups.

5)  Minor optimisations.

6)  Add inclusion guard for zahl.h.

Signed-off-by: Mattias Andrée <maandree@kth.se>

Diffstat:
MMakefile | 2+-
Mman/zerror.3 | 43+++++++++++++++++++++++++++++++++++++++++++
Msrc/allocator.c | 48++++++++++++++++++++++++++++++++++--------------
Msrc/internals.h | 56++++++++++++++++++++++++++++++++++++++++++++++++++++----
Msrc/zadd.c | 18+++++++++---------
Msrc/zand.c | 6+++---
Msrc/zbset.c | 4+---
Msrc/zcmpi.c | 4++--
Msrc/zcmpmag.c | 4++--
Msrc/zcmpu.c | 4++--
Msrc/zdivmod.c | 8++++----
Msrc/zerror.c | 21+++++++++++++++++----
Msrc/zfree.c | 8+++-----
Msrc/zgcd.c | 8++++----
Msrc/zload.c | 2+-
Msrc/zlsh.c | 8++++----
Msrc/zmodmul.c | 2+-
Msrc/zmodpow.c | 16++++++++--------
Msrc/zmodpowu.c | 12++++++------
Msrc/zmodsqr.c | 2+-
Msrc/zmul.c | 2+-
Msrc/znot.c | 6++----
Msrc/zor.c | 8++++----
Msrc/zperror.c | 8++++++--
Msrc/zpow.c | 8++++----
Msrc/zpowu.c | 6+++---
Msrc/zptest.c | 4++--
Msrc/zrand.c | 18+++++++++---------
Msrc/zrsh.c | 10+++++-----
Msrc/zsave.c | 2+-
Msrc/zset.c | 2+-
Msrc/zseti.c | 2+-
Msrc/zsets.c | 4++--
Msrc/zsplit.c | 4++--
Msrc/zsqr.c | 2+-
Msrc/zstr.c | 19++++++-------------
Msrc/zsub.c | 16++++++++--------
Msrc/ztrunc.c | 8++++----
Msrc/zxor.c | 6+++---
Mtest.c | 2+-
Mzahl.h | 16+++++++++++++---
41 files changed, 277 insertions(+), 152 deletions(-)

diff --git a/Makefile b/Makefile @@ -80,7 +80,7 @@ test-random.c: test-generate.py ./test-generate.py > test-random.c test: test.c libzahl.a test-random.c - $(CC) $(LDFLAGS) $(CFLAGS) $(CPPFLAGS) -o $@ test.c libzahl.a + $(CC) $(LDFLAGS) $(CFLAGS:--O*) -O0 $(CPPFLAGS) -o $@ test.c libzahl.a ifndef BENCHMARK_LIB benchmark: bench/benchmark.c libzahl.a diff --git a/man/zerror.3 b/man/zerror.3 @@ -37,5 +37,48 @@ The error is stored in (The error may not be stored in .I errno until this function is called.) +.TP +.B ZERROR_0_POW_0 +An attempt to calculate the zeroth power of zero was made. +This is on indeterminate form and cannot be calculated. +The closest matching +.I errno +value is +.BR EDOM . +.TP +.B ZERROR_0_DIV_0 +An attempt to divide zero by zero was made. +This is on indeterminate form and cannot be calculated. +The closest matching +.I errno +value is +.BR EDOM . +.TP +.B ZERROR_DIV_0 +An attempt to divide a non-zero value by zero was made. +This is undefined and cannot be calculated. +This occurs if a divisor or a modulator is zero, or if +zero is raised to a negative number. +The closest matching +.I errno +value is +.BR EDOM . +.TP +.B ZERROR_NEGATIVE +A function argument that must not be negative was negative. +The closest matching +.I errno +values is +.B EDOM +and +.BR EINVAL . +.SH RETURN VALUE +.B zerror +returns the error that caused libzahl a function to fail. +.SH NOTES +.I errno +is only set if +.B ZERROR_ERRNO_SET +is returned. .SH SEE ALSO .BR zperror (3) diff --git a/src/allocator.c b/src/allocator.c @@ -1,27 +1,47 @@ /* See LICENSE file for copyright and license details. */ #include "internals.h" +#include <stdio.h> + + +/* Find r such that r is a minimal power of 2 ≥ n. */ +#define NEXT_2POW(n)\ + do if (likely((n & (~n + 1)) != n)) {\ + size_t x;\ + n |= n >> 1;\ + n |= n >> 2;\ + n |= n >> 4;\ + for (i = sizeof(n), x = 8; i; i >>= 1, x <<= 1)\ + n |= n >> x;\ + n += 1;\ + } while (0) + + +#if defined(__clang__) +static inline __attribute__((optnone)) size_t +clang_warkaround_next_2pow(size_t need) +{ + size_t i; + NEXT_2POW(need); + return need; +} +#endif void libzahl_realloc(z_t a, size_t need) { - size_t i, x; + size_t i; zahl_char_t *new; - /* Find n such that n is a minimal power of 2 ≥ need. */ - if ((need & (~need + 1)) != need) { - need |= need >> 1; - need |= need >> 2; - need |= need >> 4; - for (i = sizeof(need), x = 8; i; i >>= 1, x <<= 1) - need |= need >> x; - need += 1; - } +#if defined(__clang__) + need = clang_warkaround_next_2pow(need); +#else + NEXT_2POW(need); +#endif - for (i = 0, x = need; x; x >>= 1) - i += 1; + i = libzahl_msb_nz_zu(need); - if (libzahl_pool_n[i]) { + if (likely(libzahl_pool_n[i])) { libzahl_pool_n[i]--; new = libzahl_pool[i][libzahl_pool_n[i]]; zmemcpy(new, a->chars, a->alloced); @@ -32,7 +52,7 @@ libzahl_realloc(z_t a, size_t need) if (!a->chars) { if (!errno) /* sigh... */ errno = ENOMEM; - FAILURE(errno); + libzahl_failure(errno); } } a->alloced = need; diff --git a/src/internals.h b/src/internals.h @@ -4,6 +4,7 @@ #include <string.h> #include <stdlib.h> #include <errno.h> +#include <limits.h> #define BITS_PER_CHAR 64 #define LB_BITS_PER_CHAR 6 @@ -60,13 +61,14 @@ extern size_t libzahl_pool_n[sizeof(size_t) * 8]; extern size_t libzahl_pool_alloc[sizeof(size_t) * 8]; #if defined(__GNUC__) || defined(__clang__) -# define EXPECT(value, expected) __builtin_expect(value, expected) +# define likely(value) __builtin_expect(!!(value), 1) +# define unlikely(value) __builtin_expect(!!(value), 0) #else -# define EXPECT(value, expected) (value) +# define likely(value) (value) +# define unlikely(value) (value) #endif -#define FAILURE(error) (libzahl_error = (error), longjmp(libzahl_jmp_buf, 1)) -#define zmemmove(d, s, n) memmove((d), (s), (n) * sizeof(zahl_char_t)) +#define libzahl_failure(error) (libzahl_error = (error), longjmp(libzahl_jmp_buf, 1)) #define SET_SIGNUM(a, signum) ((a)->sign = (signum)) #define SET(a, b) do { if ((a) != (b)) zset(a, b); } while (0) #define ENSURE_SIZE(a, n) do { if ((a)->alloced < (n)) libzahl_realloc(a, (n)); } while (0) @@ -74,6 +76,16 @@ extern size_t libzahl_pool_alloc[sizeof(size_t) * 8]; #define MAX(a, b) ((a) > (b) ? (a) : (b)) #define TRIM(a) for (; (a)->used && !(a)->chars[(a)->used - 1]; (a)->used--) #define TRIM_NONZERO(a) for (; !(a)->chars[(a)->used - 1]; (a)->used--) +#define TRIM_AND_ZERO(a) do { TRIM(a); if (!(a)->used) SET_SIGNUM(a, 0); } while (0) +#define znegative(a) (zsignum(a) < 0) +#define znegative1(a, b) ((zsignum(a) | zsignum(b)) < 0) +#define znegative2(a, b) ((zsignum(a) & zsignum(b)) < 0) +#define zpositive(a) (zsignum(a) > 0) +#define zpositive1(a, b) (zpositive(a) + zpositive(b) > 0) +#define zpositive2(a, b) (zsignum(a) + zsignum(b) == 2) +#define zzero1(a, b) (zzero(a) + zzero(b) > 0) +#define zzero2(a, b) (!(zsignum(a) | zsignum(b))) +#define zmemmove(d, s, n) memmove((d), (s), (n) * sizeof(zahl_char_t)) void libzahl_realloc(z_t a, size_t need); @@ -90,3 +102,39 @@ zmemset(zahl_char_t *a, register zahl_char_t v, register size_t n) while (n--) a[n] = v; } + +/* + * libzahl_msb_nz_zu + * ^^^ ^^ ^^ + * | | | + * | | \- size_t parameter + * | \- non-zero input + * \- most significant bit + */ + +#if SIZE_MAX == ULONG_MAX +# define libzahl_msb_nz_zu(x) libzahl_msb_nz_lu(x) +#else +# define libzahl_msb_nz_zu(x) libzahl_msb_nz_llu(x) +#endif + +#if defined(__GNUC__) || defined(__clang__) +# define libzahl_msb_nz_lu(x) (8 * sizeof(unsigned long int) - (size_t)__builtin_clzl(x)); +# define libzahl_msb_nz_llu(x) (8 * sizeof(unsigned long long int) - (size_t)__builtin_clzll(x)); +#else +static inline size_t +libzahl_msb_nz_lu(unsigned long int x) +{ + size_t r = 0; + for (; x; x >>= 1, r++); + return r; +} + +static inline size_t +libzahl_msb_nz_llu(unsigned long long int x) +{ + size_t r = 0; + for (; x; x >>= 1, r++); + return r; +} +#endif diff --git a/src/zadd.c b/src/zadd.c @@ -9,10 +9,10 @@ zadd_unsigned(z_t a, z_t b, z_t c) uint32_t carry[] = {0, 0}; zahl_char_t *addend; - if (EXPECT(zzero(b), 0)) { + if (unlikely(zzero(b))) { zabs(a, c); return; - } else if (EXPECT(zzero(c), 0)) { + } else if (unlikely(zzero(c))) { zabs(a, b); return; } @@ -29,7 +29,7 @@ zadd_unsigned(z_t a, z_t b, z_t c) zmemset(a->chars + a->used, 0, n - a->used); } addend = c->chars; - } else if (EXPECT(a == c, 0)) { + } else if (unlikely(a == c)) { if (a->used < b->used) { n = b->used; zmemset(a->chars + a->used, 0, n - a->used); @@ -67,15 +67,15 @@ zadd_unsigned(z_t a, z_t b, z_t c) void zadd(z_t a, z_t b, z_t c) { - if (EXPECT(zzero(b), 0)) { + if (unlikely(zzero(b))) { SET(a, c); - } else if (EXPECT(zzero(c), 0)) { + } else if (unlikely(zzero(c))) { SET(a, b); - } else if (EXPECT(b == c, 0)) { + } else if (unlikely(b == c)) { zlsh(a, b, 1); - } else if (EXPECT((zsignum(b) | zsignum(c)) < 0, 0)) { - if (zsignum(b) < 0) { - if (zsignum(c) < 0) { + } else if (unlikely(znegative1(b, c))) { + if (znegative(b)) { + if (znegative(c)) { zadd_unsigned(a, b, c); SET_SIGNUM(a, -zsignum(a)); } else { diff --git a/src/zand.c b/src/zand.c @@ -7,7 +7,7 @@ zand(z_t a, z_t b, z_t c) { size_t n; - if (EXPECT(zzero(b) || zzero(c), 0)) { + if (unlikely(zzero1(b, c))) { SET_SIGNUM(a, 0); return; } @@ -24,7 +24,7 @@ found_highest: if (a == b) { while (n--) a->chars[n] &= c->chars[n]; - } else if (EXPECT(a == c, 0)) { + } else if (unlikely(a == c)) { while (n--) a->chars[n] &= b->chars[n]; } else { @@ -33,5 +33,5 @@ found_highest: while (n--) a->chars[n] &= b->chars[n]; } - SET_SIGNUM(a, (zsignum(b) > 0 || zsignum(c) > 0) * 2 - 1); + SET_SIGNUM(a, zpositive1(b, c) * 2 - 1); } diff --git a/src/zbset.c b/src/zbset.c @@ -35,7 +35,5 @@ zbset(z_t a, z_t b, size_t bit, int action) a->chars[chars] &= ~mask; } - TRIM(a); - if (!a->used) - SET_SIGNUM(a, 0); + TRIM_AND_ZERO(a); } diff --git a/src/zcmpi.c b/src/zcmpi.c @@ -5,9 +5,9 @@ int zcmpi(z_t a, long long int b) { - if (EXPECT(!b, 0)) + if (unlikely(!b)) return zsignum(a); - if (EXPECT(zzero(a), 0)) + if (unlikely(zzero(a))) return b > 0 ? -1 : b < 0; zseti(libzahl_tmp_cmp, b); return zcmp(a, libzahl_tmp_cmp); diff --git a/src/zcmpmag.c b/src/zcmpmag.c @@ -6,9 +6,9 @@ int zcmpmag(z_t a, z_t b) { size_t i, j; - if (EXPECT(zzero(a), 0)) + if (unlikely(zzero(a))) return -!zzero(b); - if (EXPECT(zzero(b), 0)) + if (unlikely(zzero(b))) return 1; i = a->used - 1; j = b->used - 1; diff --git a/src/zcmpu.c b/src/zcmpu.c @@ -5,9 +5,9 @@ int zcmpu(z_t a, unsigned long long int b) { - if (EXPECT(!b, 0)) + if (unlikely(!b)) return zsignum(a); - if (EXPECT(zsignum(a) <= 0, 0)) + if (unlikely(zsignum(a) <= 0)) return -1; zsetu(libzahl_tmp_cmp, b); return zcmp(a, libzahl_tmp_cmp); diff --git a/src/zdivmod.c b/src/zdivmod.c @@ -15,19 +15,19 @@ zdivmod(z_t a, z_t b, z_t c, z_t d) sign = zsignum(c) * zsignum(d); - if (EXPECT(!sign, 0)) { + if (unlikely(!sign)) { if (zzero(c)) { if (zzero(d)) { - FAILURE(EDOM); /* Indeterminate form: 0 divided by 0 */ + libzahl_failure(-ZERROR_0_DIV_0); } else { SET_SIGNUM(a, 0); SET_SIGNUM(b, 0); } } else { - FAILURE(EDOM); /* Undefined form: Division by 0 */ + libzahl_failure(-ZERROR_DIV_0); } return; - } else if (EXPECT((cmpmag = zcmpmag(c, d)) <= 0, 0)) { + } else if (unlikely((cmpmag = zcmpmag(c, d)) <= 0)) { if (cmpmag == 0) { zseti(a, sign); SET_SIGNUM(b, 0); diff --git a/src/zerror.c b/src/zerror.c @@ -2,6 +2,13 @@ #include "internals.h" +#define LIST_ERRORS\ + X(ZERROR_0_POW_0, "indeterminate form: 0:th power of 0")\ + X(ZERROR_0_DIV_0, "indeterminate form: 0 divided by 0")\ + X(ZERROR_DIV_0, "undefined result: division by 0")\ + X(ZERROR_NEGATIVE, "argument must be non-negative") + + enum zerror zerror(const char **desc) { @@ -10,10 +17,16 @@ zerror(const char **desc) *desc = strerror(libzahl_error); errno = libzahl_error; return ZERROR_ERRNO_SET; - } else { - /* Current, we should not be able to get here. */ - if (desc) + } + + if (desc) { + switch (-libzahl_error) { +#define X(V, D) case V: *desc = D; break; + LIST_ERRORS +#undef X + default: abort(); - return -libzahl_error; + } } + return -libzahl_error; } diff --git a/src/zfree.c b/src/zfree.c @@ -5,15 +5,13 @@ void zfree(z_t a) { - size_t i = 0, x, j; + size_t i, x, j; zahl_char_t **new; - if (!a->chars) + if (unlikely(!a->chars)) return; - for (x = a->alloced; x; x >>= 1) - i += 1; - + i = libzahl_msb_nz_zu(a->alloced); j = libzahl_pool_n[i]++; if (j == libzahl_pool_alloc[i]) { diff --git a/src/zgcd.c b/src/zgcd.c @@ -16,22 +16,22 @@ zgcd(z_t a, z_t b, z_t c) zahl_char_t uv, bit; int neg; - if (EXPECT(!zcmp(b, c), 0)) { + if (unlikely(!zcmp(b, c))) { SET(a, b); return; } - if (EXPECT(zzero(b), 0)) { + if (unlikely(zzero(b))) { SET(a, c); return; } - if (EXPECT(zzero(c), 0)) { + if (unlikely(zzero(c))) { SET(a, b); return; } zabs(u, b); zabs(v, c); - neg = zsignum(b) < 0 && zsignum(c) < 0; + neg = znegative2(b, c); min = MIN(u->used, v->used); for (; i < min; i++) { diff --git a/src/zload.c b/src/zload.c @@ -8,7 +8,7 @@ zload(z_t a, const void *buffer) const char *buf = buffer; a->sign = *((const int *)buf), buf += sizeof(int); a->used = *((const size_t *)buf), buf += sizeof(size_t); - if (EXPECT(!!a->sign, 1)) { + if (likely(a->sign)) { ENSURE_SIZE(a, a->used); zmemcpy(a->chars, (const zahl_char_t *)buf, a->used); } diff --git a/src/zlsh.c b/src/zlsh.c @@ -8,11 +8,11 @@ zlsh(z_t a, z_t b, size_t bits) size_t i, chars, cbits; zahl_char_t carry[] = {0, 0}; - if (EXPECT(zzero(b), 0)) { + if (unlikely(zzero(b))) { SET_SIGNUM(a, 0); return; } - if (EXPECT(!bits, 0)) { + if (unlikely(!bits)) { SET(a, b); return; } @@ -22,14 +22,14 @@ zlsh(z_t a, z_t b, size_t bits) cbits = BITS_PER_CHAR - bits; ENSURE_SIZE(a, b->used + chars); - if (EXPECT(a == b, 1)) + if (likely(a == b)) zmemmove(a->chars + chars, b->chars, b->used); else zmemcpy(a->chars + chars, b->chars, b->used); zmemset(a->chars, 0, chars); a->used = b->used + chars; - if (EXPECT(bits, 1)) { /* This if statement is very important in C. */ + if (likely(bits)) { /* This if statement is very important in C. */ for (i = chars; i < a->used; i++) { carry[~i & 1] = a->chars[i] >> cbits; a->chars[i] <<= bits; diff --git a/src/zmodmul.c b/src/zmodmul.c @@ -6,7 +6,7 @@ void zmodmul(z_t a, z_t b, z_t c, z_t d) { /* TODO Montgomery modular multiplication */ - if (EXPECT(a == d, 0)) { + if (unlikely(a == d)) { zset(libzahl_tmp_modmul, d); zmul(a, b, c); zmod(a, a, libzahl_tmp_modmul); diff --git a/src/zmodpow.c b/src/zmodpow.c @@ -12,22 +12,22 @@ zmodpow(z_t a, z_t b, z_t c, z_t d) size_t i, j, n, bits; zahl_char_t x; - if (EXPECT(zsignum(c) <= 0, 0)) { + if (unlikely(zsignum(c) <= 0)) { if (zzero(c)) { if (zzero(b)) - FAILURE(EDOM); /* Indeterminate form: 0:th power of 0 */ + libzahl_failure(-ZERROR_0_POW_0); else if (zzero(d)) - FAILURE(EDOM); /* Undefined form: Division by 0 */ + libzahl_failure(-ZERROR_DIV_0); zsetu(a, 1); - } else if (zzero(b) || zzero(d)) { - FAILURE(EDOM); /* Undefined form: Division by 0 */ + } else if (zzero1(b, d)) { + libzahl_failure(-ZERROR_DIV_0); } else { SET_SIGNUM(a, 0); } return; - } else if (EXPECT(zzero(d), 0)) { - FAILURE(EDOM); /* Undefined form: Division by 0 */ - } else if (EXPECT(zzero(b), 0)) { + } else if (unlikely(zzero(d))) { + libzahl_failure(-ZERROR_DIV_0); + } else if (unlikely(zzero(b))) { SET_SIGNUM(a, 0); return; } diff --git a/src/zmodpowu.c b/src/zmodpowu.c @@ -8,17 +8,17 @@ void zmodpowu(z_t a, z_t b, unsigned long long int c, z_t d) { - if (EXPECT(!c, 0)) { + if (unlikely(!c)) { if (zzero(b)) - FAILURE(EDOM); /* Indeterminate form: 0:th power of 0 */ + libzahl_failure(-ZERROR_0_POW_0); else if (zzero(d)) - FAILURE(EDOM); /* Undefined form: Division by 0 */ + libzahl_failure(-ZERROR_DIV_0); else zsetu(a, 1); return; - } else if (EXPECT(zzero(d), 0)) { - FAILURE(EDOM); /* Undefined form: Division by 0 */ - } else if (EXPECT(zzero(b), 0)) { + } else if (unlikely(zzero(d))) { + libzahl_failure(-ZERROR_DIV_0); + } else if (unlikely(zzero(b))) { SET_SIGNUM(a, 0); return; } diff --git a/src/zmodsqr.c b/src/zmodsqr.c @@ -6,7 +6,7 @@ void zmodsqr(z_t a, z_t b, z_t c) { /* TODO What is the fastest way to do zmodsqr? */ - if (EXPECT(a == c, 0)) { + if (unlikely(a == c)) { zset(libzahl_tmp_modsqr, c); zsqr(a, b); zmod(a, a, libzahl_tmp_modsqr); diff --git a/src/zmul.c b/src/zmul.c @@ -22,7 +22,7 @@ zmul(z_t a, z_t b, z_t c) b_sign = zsignum(b); c_sign = zsignum(c); - if (EXPECT(!b_sign || !c_sign, 0)) { + if (unlikely(!b_sign || !c_sign)) { SET_SIGNUM(a, 0); return; } diff --git a/src/znot.c b/src/znot.c @@ -7,7 +7,7 @@ znot(z_t a, z_t b) { size_t bits, i; - if (EXPECT(zzero(b), 0)) { + if (unlikely(zzero(b))) { SET_SIGNUM(a, 0); return; } @@ -22,7 +22,5 @@ znot(z_t a, z_t b) if (bits) a->chars[a->used - 1] &= ((zahl_char_t)1 << bits) - 1; - TRIM(a); - if (!a->used) - SET_SIGNUM(a, 0); + TRIM_AND_ZERO(a); } diff --git a/src/zor.c b/src/zor.c @@ -7,13 +7,13 @@ zor(z_t a, z_t b, z_t c) { size_t n, m; - if (EXPECT(zzero(b), 0)) { + if (unlikely(zzero(b))) { if (zzero(c)) SET_SIGNUM(a, 0); else SET(a, c); return; - } else if (EXPECT(zzero(c), 0)) { + } else if (unlikely(zzero(c))) { SET(a, b); return; } @@ -28,7 +28,7 @@ zor(z_t a, z_t b, z_t c) zmemcpy(a->chars + n, c->chars + n, m - n); while (n--) a->chars[n] |= c->chars[n]; - } else if (EXPECT(a == c, 0)) { + } else if (unlikely(a == c)) { if (c->used < b->used) zmemcpy(a->chars + n, b->chars + n, m - n); while (n--) @@ -44,5 +44,5 @@ zor(z_t a, z_t b, z_t c) } a->used = m; - SET_SIGNUM(a, (zsignum(b) > 0 && zsignum(c) > 0) * 2 - 1); + SET_SIGNUM(a, zpositive2(b, c) * 2 - 1); } diff --git a/src/zperror.c b/src/zperror.c @@ -11,7 +11,11 @@ zperror(const char *prefix) errno = libzahl_error; perror(prefix); } else { - /* Current, we should not be able to get here. */ - abort(); + const char *desc; + zerror(&desc); + if (prefix && *prefix) + fprintf(stderr, "%s: %s\n", prefix, desc); + else + fprintf(stderr, "%s\n", desc); } } diff --git a/src/zpow.c b/src/zpow.c @@ -17,18 +17,18 @@ zpow(z_t a, z_t b, z_t c) size_t i, j, n, bits; zahl_char_t x; - if (EXPECT(zsignum(c) <= 0, 0)) { + if (unlikely(zsignum(c) <= 0)) { if (zzero(c)) { if (zzero(b)) - FAILURE(EDOM); /* Indeterminate form: 0:th power of 0 */ + libzahl_failure(-ZERROR_0_POW_0); zsetu(a, 1); } else if (zzero(b)) { - FAILURE(EDOM); /* Undefined form: Division by 0 */ + libzahl_failure(-ZERROR_DIV_0); } else { SET_SIGNUM(a, 0); } return; - } else if (EXPECT(zzero(b), 0)) { + } else if (unlikely(zzero(b))) { SET_SIGNUM(a, 0); return; } diff --git a/src/zpowu.c b/src/zpowu.c @@ -7,12 +7,12 @@ void zpowu(z_t a, z_t b, unsigned long long int c) { - if (EXPECT(!c, 0)) { + if (unlikely(!c)) { if (zzero(b)) - FAILURE(EDOM); /* Indeterminate form: 0:th power of 0 */ + libzahl_failure(-ZERROR_0_POW_0); zsetu(a, 1); return; - } else if (EXPECT(zzero(b), 0)) { + } else if (unlikely(zzero(b))) { SET_SIGNUM(a, 0); return; } diff --git a/src/zptest.c b/src/zptest.c @@ -17,7 +17,7 @@ zptest(z_t witness, z_t n, int t) size_t i, r; - if (EXPECT(zcmpu(n, 3) <= 0, 0)) { + if (unlikely(zcmpu(n, 3) <= 0)) { if (zcmpu(n, 1) <= 0) { if (witness) SET(witness, n); @@ -26,7 +26,7 @@ zptest(z_t witness, z_t n, int t) return PRIME; } } - if (EXPECT(zeven(n), 0)) { + if (unlikely(zeven(n))) { if (witness) SET(witness, n); return NONPRIME; diff --git a/src/zrand.c b/src/zrand.c @@ -27,7 +27,7 @@ zrand_get_random_bits(z_t r, size_t bits, int fd) for (n = chars * sizeof(zahl_char_t); n;) { read_just = read(fd, buf + read_total, n); if (read_just < 0) - FAILURE(errno); + libzahl_failure(errno); read_total += (size_t)read_just; n -= (size_t)read_just; } @@ -62,22 +62,22 @@ zrand(z_t r, enum zranddev dev, enum zranddist dist, z_t n) pathname = SECURE_RANDOM_PATHNAME; break; default: - abort(); + libzahl_failure(EINVAL); } - if (EXPECT(zzero(n), 0)) { + if (unlikely(zzero(n))) { SET_SIGNUM(r, 0); return; } fd = open(pathname, O_RDONLY); if (fd < 0) - FAILURE(errno); + libzahl_failure(errno); switch (dist) { case QUASIUNIFORM: - if (EXPECT(zsignum(n) < 0, 0)) - FAILURE(EDOM); /* n must be non-negative. */ + if (unlikely(znegative(n))) + libzahl_failure(-ZERROR_NEGATIVE); bits = zbits(n); zrand_get_random_bits(r, bits, fd); zadd(r, r, libzahl_const_1); @@ -86,8 +86,8 @@ zrand(z_t r, enum zranddev dev, enum zranddist dist, z_t n) break; case UNIFORM: - if (EXPECT(zsignum(n) < 0, 0)) - FAILURE(EDOM); /* n must be non-negative. */ + if (unlikely(znegative(n))) + libzahl_failure(-ZERROR_NEGATIVE); bits = zbits(n); do zrand_get_random_bits(r, bits, fd); @@ -95,7 +95,7 @@ zrand(z_t r, enum zranddev dev, enum zranddist dist, z_t n) break; default: - abort(); + libzahl_failure(EINVAL); } close(fd); diff --git a/src/zrsh.c b/src/zrsh.c @@ -7,14 +7,14 @@ zrsh(z_t a, z_t b, size_t bits) { size_t i, chars, cbits; - if (EXPECT(!bits, 0)) { + if (unlikely(!bits)) { SET(a, b); return; } chars = FLOOR_BITS_TO_CHARS(bits); - if (EXPECT(zzero(b) || chars >= b->used || zbits(b) <= bits, 0)) { + if (unlikely(zzero(b) || chars >= b->used || zbits(b) <= bits)) { SET_SIGNUM(a, 0); return; } @@ -22,16 +22,16 @@ zrsh(z_t a, z_t b, size_t bits) bits = BITS_IN_LAST_CHAR(bits); cbits = BITS_PER_CHAR - bits; - if (EXPECT(chars, 1) && EXPECT(a == b, 1)) { + if (likely(chars) && likely(a == b)) { a->used -= chars; zmemmove(a->chars, a->chars + chars, a->used); - } else if (EXPECT(a != b, 0)) { + } else if (unlikely(a != b)) { a->used = b->used - chars; ENSURE_SIZE(a, a->used); zmemcpy(a->chars, b->chars + chars, a->used); } - if (EXPECT(bits, 0)) { /* This if statement is very important in C. */ + if (unlikely(bits)) { /* This if statement is very important in C. */ a->chars[0] >>= bits; for (i = 1; i < a->used; i++) { a->chars[i - 1] |= a->chars[i] << cbits; diff --git a/src/zsave.c b/src/zsave.c @@ -9,7 +9,7 @@ zsave(z_t a, void *buffer) char *buf = buffer; *((int *)buf) = a->sign, buf += sizeof(int); *((size_t *)buf) = a->used, buf += sizeof(size_t); - if (EXPECT(!zzero(a), 1)) + if (likely(!zzero(a))) zmemcpy((zahl_char_t *)buf, a->chars, a->used); } return sizeof(int) + sizeof(size_t) + (zzero(a) ? 0 : a->used * sizeof(zahl_char_t)); diff --git a/src/zset.c b/src/zset.c @@ -5,7 +5,7 @@ void zset(z_t a, z_t b) { - if (EXPECT(b->sign == 0, 0)) { + if (unlikely(b->sign == 0)) { a->sign = 0; } else { a->sign = b->sign; diff --git a/src/zseti.c b/src/zseti.c @@ -5,7 +5,7 @@ void zseti(z_t a, long long int b) { - if (EXPECT(b >= 0, 0)) { + if (unlikely(b >= 0)) { zsetu(a, (unsigned long long int)b); } else { zsetu(a, (unsigned long long int)-b); diff --git a/src/zsets.c b/src/zsets.c @@ -13,12 +13,12 @@ zsets(z_t a, const char *str) str += neg || (*str == '+'); - if (EXPECT(!*str, 0)) { + if (unlikely(!*str)) { errno = EINVAL; return -1; } for (str_end = str; *str_end; str_end++) { - if (EXPECT(!isdigit(*str_end), 0)) { + if (unlikely(!isdigit(*str_end))) { errno = EINVAL; return -1; } diff --git a/src/zsplit.c b/src/zsplit.c @@ -5,13 +5,13 @@ void zsplit(z_t high, z_t low, z_t a, size_t delim) { - if (EXPECT(zzero(a), 0)) { + if (unlikely(zzero(a))) { SET_SIGNUM(high, 0); SET_SIGNUM(low, 0); return; } - if (EXPECT(high == a, 0)) { + if (unlikely(high == a)) { ztrunc(low, a, delim); zrsh(high, a, delim); } else { diff --git a/src/zsqr.c b/src/zsqr.c @@ -13,7 +13,7 @@ zsqr(z_t a, z_t b) z_t z0, z1, z2, high, low; int sign; - if (EXPECT(zzero(b), 0)) { + if (unlikely(zzero(b))) { SET_SIGNUM(a, 0); return; } diff --git a/src/zstr.c b/src/zstr.c @@ -16,16 +16,12 @@ char * zstr(z_t a, char *b) { char buf[19 + 1]; - size_t n, len; + size_t n, len, neg; char overridden = 0; - int neg; if (zzero(a)) { - if (!b) { - b = malloc(2); - if (!b) - FAILURE(errno); - } + if (!b && !(b = malloc(2))) + libzahl_failure(errno); b[0] = '0'; b[1] = 0; return b; @@ -33,13 +29,10 @@ zstr(z_t a, char *b) n = zstr_length(a, 10); - if (!b) { - b = malloc(n + 1); - if (!b) - FAILURE(errno); - } + if (!b && !(b = malloc(n + 1))) + libzahl_failure(errno); - neg = zsignum(a) < 0; + neg = znegative(a); zabs(num, a); b[0] = '-'; b += neg; diff --git a/src/zsub.c b/src/zsub.c @@ -10,11 +10,11 @@ zsub_unsigned(z_t a, z_t b, z_t c) size_t i, n; int magcmp; - if (EXPECT(zzero(b), 0)) { + if (unlikely(zzero(b))) { zabs(a, c); zneg(a, a); return; - } else if (EXPECT(zzero(c), 0)) { + } else if (unlikely(zzero(c))) { zabs(a, b); return; } @@ -61,15 +61,15 @@ zsub_unsigned(z_t a, z_t b, z_t c) void zsub(z_t a, z_t b, z_t c) { - if (EXPECT(b == c, 0)) { + if (unlikely(b == c)) { SET_SIGNUM(a, 0); - } else if (EXPECT(zzero(b), 0)) { + } else if (unlikely(zzero(b))) { zneg(a, c); - } else if (EXPECT(zzero(c), 0)) { + } else if (unlikely(zzero(c))) { SET(a, b); - } else if (EXPECT((zsignum(b) | zsignum(c)) < 0, 0)) { - if (zsignum(b) < 0) { - if (zsignum(c) < 0) { + } else if (unlikely(znegative1(b, c))) { + if (znegative(b)) { + if (znegative(c)) { zsub_unsigned(a, c, b); } else { zadd_unsigned(a, b, c); diff --git a/src/ztrunc.c b/src/ztrunc.c @@ -8,7 +8,7 @@ ztrunc(z_t a, z_t b, size_t bits) zahl_char_t mask = 1; size_t chars; - if (EXPECT(zzero(b), 0)) { + if (unlikely(zzero(b))) { SET_SIGNUM(a, 0); return; } @@ -16,14 +16,14 @@ ztrunc(z_t a, z_t b, size_t bits) chars = CEILING_BITS_TO_CHARS(bits); a->sign = b->sign; a->used = MIN(chars, b->used); - if (EXPECT(a->used < chars, 0)) + if (unlikely(a->used < chars)) bits = 0; - if (EXPECT(a != b, 1)) { + if (likely(a != b)) { ENSURE_SIZE(a, a->used); zmemcpy(a->chars, b->chars, a->used); } bits = BITS_IN_LAST_CHAR(bits); - if (EXPECT(!!bits, 1)) { + if (likely(bits)) { mask <<= bits; mask -= 1; a->chars[a->used - 1] &= mask; diff --git a/src/zxor.c b/src/zxor.c @@ -7,13 +7,13 @@ zxor(z_t a, z_t b, z_t c) { size_t n, m; - if (EXPECT(zzero(b), 0)) { + if (unlikely(zzero(b))) { if (zzero(c)) SET_SIGNUM(a, 0); else SET(a, c); return; - } else if (EXPECT(zzero(c), 0)) { + } else if (unlikely(zzero(c))) { SET(a, b); return; } @@ -28,7 +28,7 @@ zxor(z_t a, z_t b, z_t c) zmemcpy(a->chars + n, c->chars + n, m - n); while (n--) a->chars[n] ^= c->chars[n]; - } else if (EXPECT(a == c, 0)) { + } else if (unlikely(a == c)) { if (c->used < b->used) zmemcpy(a->chars + n, b->chars + n, m - n); while (n--) diff --git a/test.c b/test.c @@ -112,7 +112,7 @@ main(void) assert(zodd(_2), == 0); assert(zzero(_2), == 0); assert(zsignum(_2), == 1); - + assert((zneg(_2, _2), zsignum(_2)), == -1); zneg(_2, _2); assert(zsignum(_2), == 1); diff --git a/zahl.h b/zahl.h @@ -3,6 +3,9 @@ /* Warning: libzahl is not thread-safe. */ /* Caution: Do not use libzahl for cryptographic applications, use a specialised library. */ +#ifndef ZAHL_H +#define ZAHL_H + #include <stddef.h> #include <setjmp.h> @@ -27,7 +30,11 @@ enum zranddev { FAST_RANDOM = 0, SECURE_RANDOM }; enum zranddist { QUASIUNIFORM = 0, UNIFORM }; enum zerror { - ZERROR_ERRNO_SET = 0 + ZERROR_ERRNO_SET = 0, /* Please refer to errno. */ + ZERROR_0_POW_0, /* Indeterminate form: 0:th power of 0. (Translatable to EDOM.) */ + ZERROR_0_DIV_0, /* Indeterminate form: 0 divided by 0. (Translatable to EDOM.) */ + ZERROR_DIV_0, /* Undefined result: Division by 0. (Translatable to EDOM.) */ + ZERROR_NEGATIVE /* Argument must be non-negative. (Translatable to EDOM or EINVAL.) */ }; @@ -160,7 +167,7 @@ zlsb(z_t a) return SIZE_MAX; for (; !a->chars[i]; i++); i *= 8 * sizeof(zahl_char_t); - i += __builtin_ctzll(a->chars[i]); + i += (size_t)__builtin_ctzll(a->chars[i]); return i; } #else @@ -190,7 +197,7 @@ zbits(z_t a) return 1; while (!a->chars[a->used - 1]) a->used--; /* TODO should not be necessary */ rc = a->used * 8 * sizeof(zahl_char_t); - rc -= __builtin_clzll(a->chars[a->used - 1]); + rc -= (size_t)__builtin_clzll(a->chars[a->used - 1]); return rc; } #else @@ -208,3 +215,6 @@ zbits(z_t a) return rc; } #endif + + +#endif