const std = @import("std"); const Log2Int = std.math.Log2Int; const Allocator = std.mem.Allocator; pub fn RingBuffer(comptime T: type) type { return struct { const Self = @This(); const Index = usize; const max_capacity = (@as(Index, 1) << @typeInfo(Index).Int.bits - 1) - 1; // half the range of index type const log = std.log.scoped(.RingBuffer); read: Index, write: Index, buf: []T, const Error = error{buffer_full}; pub fn init(buf: []T) Self { std.debug.assert(std.math.isPowerOfTwo(buf.len)); // capacity must be a power of two std.debug.assert(buf.len <= max_capacity); return .{ .read = 0, .write = 0, .buf = buf }; } pub fn push(self: *Self, value: T) Error!void { if (self.isFull()) return error.buffer_full; defer self.write += 1; self.buf[self.mask(self.write)] = value; } pub fn pop(self: *Self) ?T { if (self.isEmpty()) return null; defer self.read += 1; return self.buf[self.mask(self.read)]; } /// Returns the number of entries read pub fn copy(self: *const Self, cpy: []T) Index { const count = @min(self.len(), cpy.len); var start: Index = self.read; for (cpy, 0..) |*v, i| { if (i >= count) break; v.* = self.buf[self.mask(start)]; start += 1; } return count; } fn len(self: *const Self) Index { return self.write - self.read; } fn isFull(self: *const Self) bool { return self.len() == self.buf.len; } fn isEmpty(self: *const Self) bool { return self.read == self.write; } fn mask(self: *const Self, idx: Index) Index { return idx & (self.buf.len - 1); } }; } // Sign-Extend value of type `T` to type `U` pub fn sext(comptime T: type, comptime U: type, value: T) T { // U must have less bits than T comptime std.debug.assert(@typeInfo(U).Int.bits <= @typeInfo(T).Int.bits); const iT = std.meta.Int(.signed, @typeInfo(T).Int.bits); const ExtU = if (@typeInfo(U).Int.signedness == .unsigned) T else iT; const shift_amt: Log2Int(T) = @intCast(@typeInfo(T).Int.bits - @typeInfo(U).Int.bits); return @bitCast(@as(iT, @bitCast(@as(ExtU, @as(U, @truncate(value))) << shift_amt)) >> shift_amt); } /// See https://godbolt.org/z/W3en9Eche pub inline fn rotr(comptime T: type, x: T, r: anytype) T { if (@typeInfo(T).Int.signedness == .signed) @compileError("cannot rotate signed integer"); const ar: Log2Int(T) = @intCast(@mod(r, @typeInfo(T).Int.bits)); return x >> ar | x << (1 +% ~ar); }