const std = @import("std"); pub fn RingBuffer(comptime T: type) type { return struct { const Self = @This(); const Index = usize; const Atomic = std.atomic.Atomic; 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: Atomic(Index), write: Atomic(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 = Atomic(Index).init(0), .write = Atomic(Index).init(0), .buf = buf, }; } pub fn push(self: *Self, value: T) Error!void { const read_idx = self.read.load(.Acquire); const write_idx = self.write.load(.Acquire); // Check to see if Queue is full if (write_idx - read_idx == self.buf.len) return Error.buffer_full; self.buf[self.mask(write_idx)] = value; std.atomic.fence(.Release); self.write.store(write_idx + 1, .Release); } pub fn pop(self: *Self) ?T { const read_idx = self.read.load(.Acquire); const write_idx = self.write.load(.Acquire); if (read_idx == write_idx) return null; std.atomic.fence(.Acquire); const value = self.buf[self.mask(self.read)]; std.atomic.fence(.Release); self.read.store(read_idx + 1, .Release); return value; } pub fn len(self: *const Self) Index { const read_idx = self.read.load(.Acquire); const write_idx = self.write.load(.Acquire); return write_idx - read_idx; } fn mask(self: *const Self, idx: Index) Index { return idx & (self.buf.len - 1); } }; }