Type Function Deque [src]
Alias for std.deque.Deque
A contiguous, growable, double-ended queue.
Pushing/popping items from either end of the queue is O(1).
Prototype
pub fn Deque(comptime T: type) type Parameters
T: type Source
pub fn Deque(comptime T: type) type {
return struct {
const Self = @This();
/// A ring buffer.
buffer: []T,
/// The index in buffer where the first item in the logical deque is stored.
head: usize,
/// The number of items stored in the logical deque.
len: usize,
/// A Deque containing no elements.
pub const empty: Self = .{
.buffer = &.{},
.head = 0,
.len = 0,
};
/// Initialize with capacity to hold `capacity` elements.
/// The resulting capacity will equal `capacity` exactly.
/// Deinitialize with `deinit`.
pub fn initCapacity(gpa: Allocator, capacity: usize) Allocator.Error!Self {
var deque: Self = .empty;
try deque.ensureTotalCapacityPrecise(gpa, capacity);
return deque;
}
/// Initialize with externally-managed memory. The buffer determines the
/// capacity and the deque is initially empty.
///
/// When initialized this way, all functions that accept an Allocator
/// argument cause illegal behavior.
pub fn initBuffer(buffer: []T) Self {
return .{
.buffer = buffer,
.head = 0,
.len = 0,
};
}
/// Release all allocated memory.
pub fn deinit(deque: *Self, gpa: Allocator) void {
gpa.free(deque.buffer);
deque.* = undefined;
}
/// Modify the deque so that it can hold at least `new_capacity` items.
/// Implements super-linear growth to achieve amortized O(1) push/pop operations.
/// Invalidates element pointers if additional memory is needed.
pub fn ensureTotalCapacity(deque: *Self, gpa: Allocator, new_capacity: usize) Allocator.Error!void {
if (deque.buffer.len >= new_capacity) return;
return deque.ensureTotalCapacityPrecise(gpa, growCapacity(deque.buffer.len, new_capacity));
}
/// If the current capacity is less than `new_capacity`, this function will
/// modify the deque so that it can hold exactly `new_capacity` items.
/// Invalidates element pointers if additional memory is needed.
pub fn ensureTotalCapacityPrecise(deque: *Self, gpa: Allocator, new_capacity: usize) Allocator.Error!void {
if (deque.buffer.len >= new_capacity) return;
const old_buffer = deque.buffer;
if (gpa.remap(old_buffer, new_capacity)) |new_buffer| {
// If the items wrap around the end of the buffer we need to do
// a memcpy to prevent a gap after resizing the buffer.
if (deque.head > old_buffer.len - deque.len) {
// The gap splits the items in the deque into head and tail parts.
// Choose the shorter part to copy.
const head = new_buffer[deque.head..old_buffer.len];
const tail = new_buffer[0 .. deque.len - head.len];
if (head.len > tail.len and new_buffer.len - old_buffer.len > tail.len) {
@memcpy(new_buffer[old_buffer.len..][0..tail.len], tail);
} else {
// In this case overlap is possible if e.g. the capacity increase is 1
// and head.len is greater than 1.
deque.head = new_buffer.len - head.len;
@memmove(new_buffer[deque.head..][0..head.len], head);
}
}
deque.buffer = new_buffer;
} else {
const new_buffer = try gpa.alloc(T, new_capacity);
if (deque.head < old_buffer.len - deque.len) {
@memcpy(new_buffer[0..deque.len], old_buffer[deque.head..][0..deque.len]);
} else {
const head = old_buffer[deque.head..];
const tail = old_buffer[0 .. deque.len - head.len];
@memcpy(new_buffer[0..head.len], head);
@memcpy(new_buffer[head.len..][0..tail.len], tail);
}
deque.head = 0;
deque.buffer = new_buffer;
gpa.free(old_buffer);
}
}
/// Modify the deque so that it can hold at least `additional_count` **more** items.
/// Invalidates element pointers if additional memory is needed.
pub fn ensureUnusedCapacity(
deque: *Self,
gpa: Allocator,
additional_count: usize,
) Allocator.Error!void {
return deque.ensureTotalCapacity(gpa, try addOrOom(deque.len, additional_count));
}
/// Add one item to the front of the deque.
///
/// Invalidates element pointers if additional memory is needed.
pub fn pushFront(deque: *Self, gpa: Allocator, item: T) error{OutOfMemory}!void {
try deque.ensureUnusedCapacity(gpa, 1);
deque.pushFrontAssumeCapacity(item);
}
/// Add one item to the front of the deque.
///
/// Never invalidates element pointers.
///
/// If the deque lacks unused capacity for the additional item, returns
/// `error.OutOfMemory`.
pub fn pushFrontBounded(deque: *Self, item: T) error{OutOfMemory}!void {
if (deque.buffer.len - deque.len == 0) return error.OutOfMemory;
return deque.pushFrontAssumeCapacity(item);
}
/// Add one item to the front of the deque.
///
/// Never invalidates element pointers.
///
/// Asserts that the deque can hold one additional item.
pub fn pushFrontAssumeCapacity(deque: *Self, item: T) void {
assert(deque.len < deque.buffer.len);
if (deque.head == 0) {
deque.head = deque.buffer.len;
}
deque.head -= 1;
deque.buffer[deque.head] = item;
deque.len += 1;
}
/// Add one item to the back of the deque.
///
/// Invalidates element pointers if additional memory is needed.
pub fn pushBack(deque: *Self, gpa: Allocator, item: T) error{OutOfMemory}!void {
try deque.ensureUnusedCapacity(gpa, 1);
deque.pushBackAssumeCapacity(item);
}
/// Add one item to the back of the deque.
///
/// Never invalidates element pointers.
///
/// If the deque lacks unused capacity for the additional item, returns
/// `error.OutOfMemory`.
pub fn pushBackBounded(deque: *Self, item: T) error{OutOfMemory}!void {
if (deque.buffer.len - deque.len == 0) return error.OutOfMemory;
deque.pushBackAssumeCapacity(item);
}
/// Add one item to the back of the deque.
///
/// Never invalidates element pointers.
///
/// Asserts that the deque can hold one additional item.
pub fn pushBackAssumeCapacity(deque: *Self, item: T) void {
assert(deque.len < deque.buffer.len);
const buffer_index = deque.bufferIndex(deque.len);
deque.buffer[buffer_index] = item;
deque.len += 1;
}
/// Return the first item in the deque or null if empty.
pub fn front(deque: *const Self) ?T {
if (deque.len == 0) return null;
return deque.buffer[deque.head];
}
/// Return the last item in the deque or null if empty.
pub fn back(deque: *const Self) ?T {
if (deque.len == 0) return null;
return deque.buffer[deque.bufferIndex(deque.len - 1)];
}
/// Return the item at the given index in the deque.
///
/// The first item in the queue is at index 0.
///
/// Asserts that the index is in-bounds.
pub fn at(deque: *const Self, index: usize) T {
assert(index < deque.len);
return deque.buffer[deque.bufferIndex(index)];
}
/// Remove and return the first item in the deque or null if empty.
pub fn popFront(deque: *Self) ?T {
if (deque.len == 0) return null;
const pop_index = deque.head;
deque.head = deque.bufferIndex(1);
deque.len -= 1;
return deque.buffer[pop_index];
}
/// Remove and return the last item in the deque or null if empty.
pub fn popBack(deque: *Self) ?T {
if (deque.len == 0) return null;
deque.len -= 1;
return deque.buffer[deque.bufferIndex(deque.len)];
}
pub const Iterator = struct {
deque: *const Self,
index: usize,
pub fn next(it: *Iterator) ?T {
if (it.index < it.deque.len) {
defer it.index += 1;
return it.deque.at(it.index);
} else {
return null;
}
}
};
/// Iterates over all items in the deque in order from front to back.
pub fn iterator(deque: *const Self) Iterator {
return .{ .deque = deque, .index = 0 };
}
/// Returns the index in `buffer` where the element at the given
/// index in the logical deque is stored.
fn bufferIndex(deque: *const Self, index: usize) usize {
// This function is written in this way to avoid overflow and
// expensive division.
const head_len = deque.buffer.len - deque.head;
if (index < head_len) {
return deque.head + index;
} else {
return index - head_len;
}
}
const init_capacity: comptime_int = @max(1, std.atomic.cache_line / @sizeOf(T));
/// Called when memory growth is necessary. Returns a capacity larger than
/// minimum that grows super-linearly.
fn growCapacity(current: usize, minimum: usize) usize {
var new = current;
while (true) {
new +|= new / 2 + init_capacity;
if (new >= minimum) return new;
}
}
};
}