master
  1const std = @import("std");
  2const expect = std.testing.expect;
  3const expectEqual = std.testing.expectEqual;
  4const builtin = @import("builtin");
  5
  6fn ShardedTable(comptime Key: type, comptime mask_bit_count: comptime_int, comptime V: type) type {
  7    const key_bits = @typeInfo(Key).int.bits;
  8    std.debug.assert(Key == std.meta.Int(.unsigned, key_bits));
  9    std.debug.assert(key_bits >= mask_bit_count);
 10    const shard_key_bits = mask_bit_count;
 11    const ShardKey = std.meta.Int(.unsigned, mask_bit_count);
 12    const shift_amount = key_bits - shard_key_bits;
 13    return struct {
 14        const Self = @This();
 15        shards: [1 << shard_key_bits]?*Node,
 16
 17        pub fn create() Self {
 18            return Self{ .shards = [_]?*Node{null} ** (1 << shard_key_bits) };
 19        }
 20
 21        fn getShardKey(key: Key) ShardKey {
 22            // https://github.com/ziglang/zig/issues/1544
 23            // this special case is needed because you can't u32 >> 32.
 24            if (ShardKey == u0) return 0;
 25
 26            // this can be u1 >> u0
 27            const shard_key = key >> shift_amount;
 28
 29            // TODO: https://github.com/ziglang/zig/issues/1544
 30            // This cast could be implicit if we teach the compiler that
 31            // u32 >> 30 -> u2
 32            return @as(ShardKey, @intCast(shard_key));
 33        }
 34
 35        pub fn put(self: *Self, node: *Node) void {
 36            const shard_key = Self.getShardKey(node.key);
 37            node.next = self.shards[shard_key];
 38            self.shards[shard_key] = node;
 39        }
 40
 41        pub fn get(self: *Self, key: Key) ?*Node {
 42            const shard_key = Self.getShardKey(key);
 43            var maybe_node = self.shards[shard_key];
 44            while (maybe_node) |node| : (maybe_node = node.next) {
 45                if (node.key == key) return node;
 46            }
 47            return null;
 48        }
 49
 50        pub const Node = struct {
 51            key: Key,
 52            value: V,
 53            next: ?*Node,
 54
 55            pub fn init(self: *Node, key: Key, value: V) void {
 56                self.key = key;
 57                self.value = value;
 58                self.next = null;
 59            }
 60        };
 61    };
 62}
 63
 64test "sharded table" {
 65    if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
 66    if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
 67    if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
 68    if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
 69
 70    // realistic 16-way sharding
 71    try testShardedTable(u32, 4, 8);
 72
 73    try testShardedTable(u5, 0, 32); // ShardKey == u0
 74    try testShardedTable(u5, 2, 32);
 75    try testShardedTable(u5, 5, 32);
 76
 77    try testShardedTable(u1, 0, 2);
 78    try testShardedTable(u1, 1, 2); // this does u1 >> u0
 79
 80    try testShardedTable(u0, 0, 1);
 81}
 82
 83fn testShardedTable(comptime Key: type, comptime mask_bit_count: comptime_int, comptime node_count: comptime_int) !void {
 84    const Table = ShardedTable(Key, mask_bit_count, void);
 85
 86    var table = Table.create();
 87    var node_buffer: [node_count]Table.Node = undefined;
 88    for (&node_buffer, 0..) |*node, i| {
 89        const key = @as(Key, @intCast(i));
 90        try expect(table.get(key) == null);
 91        node.init(key, {});
 92        table.put(node);
 93    }
 94
 95    for (&node_buffer, 0..) |*node, i| {
 96        try expect(table.get(@as(Key, @intCast(i))) == node);
 97    }
 98}
 99
100// #2225
101test "comptime shr of BigInt" {
102    comptime {
103        const n0 = 0xdeadbeef0000000000000000;
104        try expect(n0 >> 64 == 0xdeadbeef);
105        const n1 = 17908056155735594659;
106        try expect(n1 >> 64 == 0);
107    }
108}
109
110test "comptime shift safety check" {
111    _ = @as(usize, 42) << @sizeOf(usize);
112}
113
114test "Saturating Shift Left where lhs is of a computed type" {
115    if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
116    if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
117    if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
118    if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
119    if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
120
121    const S = struct {
122        pub fn FixedPoint(comptime ValueType: type) type {
123            return struct {
124                value: ValueType,
125                exponent: ShiftType,
126
127                const ShiftType = @Int(.signed, @typeInfo(std.math.Log2Int(ValueType)).int.bits);
128
129                pub fn shiftExponent(self: @This(), shift: ShiftType) @This() {
130                    const shiftAbs = @abs(shift);
131                    return .{ .value = if (shift >= 0) self.value >> shiftAbs else self.value <<| shiftAbs, .exponent = self.exponent + shift };
132                }
133            };
134        }
135    };
136
137    const FP = S.FixedPoint(i32);
138
139    const value = (FP{
140        .value = 1,
141        .exponent = 1,
142    }).shiftExponent(-1);
143
144    try expect(value.value == 2);
145    try expect(value.exponent == 0);
146}
147
148test "Saturating Shift Left" {
149    if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
150    if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
151    if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest;
152    if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
153    if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
154    if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
155
156    const S = struct {
157        fn shlSat(x: anytype, y: std.math.Log2Int(@TypeOf(x))) @TypeOf(x) {
158            return x <<| y;
159        }
160
161        fn testType(comptime T: type) !void {
162            comptime var rhs: std.math.Log2Int(T) = 0;
163            inline while (true) : (rhs += 1) {
164                comptime var lhs: T = std.math.minInt(T);
165                inline while (true) : (lhs += 1) {
166                    try expectEqual(lhs <<| rhs, shlSat(lhs, rhs));
167                    if (lhs == std.math.maxInt(T)) break;
168                }
169                if (rhs == @bitSizeOf(T) - 1) break;
170            }
171        }
172    };
173
174    try S.testType(u2);
175    try S.testType(i2);
176    try S.testType(u3);
177    try S.testType(i3);
178    try S.testType(u4);
179    try S.testType(i4);
180
181    try expectEqual(0xfffffffffffffff0fffffffffffffff0, S.shlSat(@as(u128, 0x0fffffffffffffff0fffffffffffffff), 4));
182    try expectEqual(0xffffffffffffffffffffffffffffffff, S.shlSat(@as(u128, 0x0fffffffffffffff0fffffffffffffff), 5));
183    try expectEqual(-0x80000000000000000000000000000000, S.shlSat(@as(i128, -0x0fffffffffffffff0fffffffffffffff), 5));
184
185    try expectEqual(51146728248377216718956089012931236753385031969422887335676427626502090568823039920051095192592252455482604439493126109519019633529459266458258243583, S.shlSat(@as(i495, 0x2fe6bc5448c55ce18252e2c9d44777505dfe63ff249a8027a6626c7d8dd9893fd5731e51474727be556f757facb586a4e04bbc0148c6c7ad692302f46fbd), 0x31));
186    try expectEqual(-57896044618658097711785492504343953926634992332820282019728792003956564819968, S.shlSat(@as(i256, -0x53d4148cee74ea43477a65b3daa7b8fdadcbf4508e793f4af113b8d8da5a7eb6), 0x91));
187    try expectEqual(170141183460469231731687303715884105727, S.shlSat(@as(i128, 0x2fe6bc5448c55ce18252e2c9d4477750), 0x31));
188    try expectEqual(0, S.shlSat(@as(i128, 0), 127));
189}