File: bits.ha

package info (click to toggle)
hare 0.25.2-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 6,948 kB
  • sloc: asm: 1,264; makefile: 123; sh: 114; lisp: 101
file content (181 lines) | stat: -rw-r--r-- 5,317 bytes parent folder | download
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
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
// SPDX-License-Identifier: MPL-2.0
// (c) Hare authors <https://harelang.org>

use types;


// Rotates a 32-bit unsigned integer left by k bits. k may be negative to rotate
// right instead, or see [[rotr32]].
export fn rotl32(x: u32, k: int) u32 = {
	const n = 32u32;
	const s = k: u32 & (n - 1);
	return x << s | x >> (n - s);
};

// Rotates a 32-bit unsigned integer right by k bits. k may be negative to
// rotate left instead, or see [[rotl32]].
export fn rotr32(x: u32, k: int) u32 = rotl32(x, -k);

@test fn lrot32() void = {
	let a = 0b11110000111100001111000011110000u32;
	assert(rotl32(a, 2) == 0b11000011110000111100001111000011u32);
	assert(rotl32(a, -2) == 0b00111100001111000011110000111100u32);
	assert(rotl32(a, 32) == 0b11110000111100001111000011110000u32);
	assert(rotl32(a, 64) == 0b11110000111100001111000011110000u32);
};

// Rotates a 64-bit unsigned integer left by k bits. k may be negative to rotate
// right instead, or see [[rotr64]].
export fn rotl64(x: u64, k: int) u64 = {
	const n = 64u64;
	const s = k: u64 & (n - 1);
	return x << s | x >> (n - s);
};

// Rotates a 64-bit unsigned integer right by k bits. k may be negative to rotate
// left instead, or see [[rotl64]].
export fn rotr64(x: u64, k: int) u64 = rotl64(x, -k);

@test fn lrot64() void = {
	let a = 1u64;
	assert(rotl64(a, 1) == 0b10);
	assert(rotl64(a, -1) == 0b1000000000000000000000000000000000000000000000000000000000000000);
	assert(rotl64(a, 39) == (1u64 << 39));

	let a = 0b1111000011110000111100001111000011110000111100001111000011110000u64;
	assert(rotl64(a, 64) == a);
	assert(rotl64(a, 0) == a);
	assert(rotl64(a, 2) == 0b1100001111000011110000111100001111000011110000111100001111000011u64);
	assert(rotl64(a, -2) == 0b0011110000111100001111000011110000111100001111000011110000111100u64);

};

// Stores the xor of 'a' and 'b' into 'dest'. All parameters must have the same
// length. 'dest' may be the same slice as 'a' and/or 'b'.
export fn xor(dest: []u8, a: []u8, b: []u8) void = {
	assert(len(dest) == len(a) && len(dest) == len(b),
		"dest, a and b must have the same length");

	for (let i = 0z; i < len(dest); i += 1) {
		dest[i] = a[i] ^ b[i];
	};
};

// Compare two byte slices in constant time. The slices must have the same
// length.
//
// Returns 1 if the two slices have the same contents, 0 otherwise.
export fn eqslice(x: []u8, y: []u8) int = {
	assert(len(x) == len(y), "slices must have the same length");
	let v: u8 = 0;
	for (let i = 0z; i < len(x); i += 1) {
		v |= x[i] ^ y[i];
	};
	return equ8(v, 0);
};

@test fn eqslice() void = {
	assert(eqslice([], []) == 1);
	assert(eqslice([0], [0]) == 1);
	assert(eqslice([1], [0]) == 0);
	assert(eqslice([1, 0], [0, 0]) == 0);
	assert(eqslice([0, 0], [0, 0]) == 1);

	assert(eqslice([0x12, 0xAB], [0x12, 0xAB]) == 1);
	assert(eqslice([0x12, 0xAB], [0x12, 0xAC]) == 0);
	assert(eqslice([0x12, 0xAB], [0x11, 0xAB]) == 0);
};

// Compare two bytes in constant time. Returns 1 if the bytes are the same
// value, 0 otherwise.
export fn equ8(x: u8, y: u8) int = ((((x ^ y): u32) - 1) >> 31): int;

// Returns x if ctl == 1 and y if ctl == 0.
export fn muxu32(ctl: u32, x: u32, y: u32) u32 = y ^ (-ctl & (x ^ y));

@test fn muxu32() void = {
	assert(muxu32(1, 0x4, 0xff) == 0x4);
	assert(muxu32(0, 0x4, 0xff) == 0xff);
};

// Negates first bit.
export fn notu32(x: u32) u32 = x ^ 1;

// Compares 'x' and 'y'. Returns 1 if they are equal or 0 otherwise.
export fn equ32(x: u32, y: u32) u32 = {
	let q = x ^ y;
	return ((q | -q) >> 31) ^ 1;
};

@test fn equ32() void = {
	assert(equ32(0x4f, 0x4f) == 1);
	assert(equ32(0x4f, 0x0) == 0);
	assert(equ32(0x2, 0x6) == 0);
};

// Returns 1 if 'x' is zero or 0 if not.
export fn eq0u32(x: u32) u32 = {
	return ~(x | -x) >> 31;
};

@test fn eq0u32() void = {
	assert(eq0u32(0) == 1);
	assert(eq0u32(1) == 0);
	assert(eq0u32(0x1234) == 0);
	assert(eq0u32(types::U32_MAX) == 0);
};

// Returns 1 if x != y and 0 otherwise.
export fn nequ32(x: u32, y: u32) u32 = {
	let q = x ^ y;
	return (q | -q) >> 31;
};

// Returns 1 if x > y and 0 otherwise.
export fn gtu32(x: u32, y: u32) u32 = {
	let z: u32 = y - x;
	return (z ^ ((x ^ y) & (x ^ z))) >> 31;
};

@test fn gtu32() void = {
	assert(gtu32(1, 0) == 1);
	assert(gtu32(0, 1) == 0);
	assert(gtu32(0, 0) == 0);

	assert(gtu32(0xf3, 0xf2) == 1);
	assert(gtu32(0x20, 0xff) == 0);
	assert(gtu32(0x23, 0x23) == 0);
};

// Returns 1 if x >= y and 0 otherwise.
export fn geu32(x: u32, y: u32) u32 = notu32(gtu32(y, x));

// Returns 1 if x < y and 0 otherwise.
export fn ltu32(x: u32, y: u32) u32 = gtu32(y, x);

// Returns 1 if x <= y and 0 otherwise.
export fn leu32(x: u32, y: u32) u32 = notu32(gtu32(x, y));

// Compares 'x' with 'y'. Returns -1 if x < y, 0 if x == y and 1 if x > x.
export fn cmpu32(x: u32, y: u32) i32 = gtu32(x, y): i32 | -(gtu32(y, x): i32);

@test fn cmpu32() void = {
	assert(cmpu32(0, 0) == 0);
	assert(cmpu32(0x34, 0x34) == 0);

	assert(cmpu32(0x12, 0x34) == -1);
	assert(cmpu32(0x87, 0x34) == 1);
};

// Multiplies two u32 and returns result as u64.
export fn mulu32(x: u32, y: u32) u64 = x: u64 * y: u64;

// Copies 'src' to 'dest' if 'ctl' == 1
export fn ccopyu32(ctl: u32, dest: []u32, src: const []u32) void = {
	for (let i = 0z; i < len(dest); i += 1) {
		const x = src[i];
		const y = dest[i];

		dest[i] = muxu32(ctl, x, y);
	};
};