Add mouse input, axis mappings, and an input demo (closes #63)
continuous-integration/drone/push Build is passing Details

This commit is contained in:
kayomn 2024-07-25 01:11:58 +01:00
parent 0f2950a630
commit 9f0f565b0b
12 changed files with 355 additions and 88 deletions

2
.vscode/launch.json vendored
View File

@ -5,7 +5,7 @@
"name": "Runner",
"type": "gdb",
"request": "launch",
"target": "${workspaceRoot}/demos/effects.out",
"target": "${workspaceRoot}/demos/inputs.out",
"cwd": "${workspaceRoot}/demos/",
"valuesFormatting": "prettyPrinters",
"preLaunchTask": "Build All"

View File

@ -208,7 +208,15 @@ pub fn build(b: *std.Build) !void {
});
const ona_module = try project.add_module(b, "ona", .{});
const hid_module = try project.add_module(b, "hid", .{});
const hid_module = try project.add_module(b, "hid", .{
.imports = &.{
.{
.name = "ona",
.module = ona_module,
},
},
});
const gfx_module = try project.add_module(b, "gfx", .{
.imports = &.{
@ -223,7 +231,7 @@ pub fn build(b: *std.Build) !void {
},
.{
.name = "input",
.name = "hid",
.module = hid_module,
},
},

View File

@ -16,10 +16,10 @@ const Effects = struct {
crt_effect: gfx.Effect = .default,
};
fn load(display: ona.Write(gfx.Display), actors: ona.Write(Effects), assets: ona.Write(gfx.Assets)) !void {
fn load(display: ona.Write(gfx.Display), effects: ona.Write(Effects), assets: ona.Write(gfx.Assets)) !void {
display.state.width, display.state.height = .{1280, 720};
actors.state.render_texture = try assets.state.load_texture(.{
effects.state.render_texture = try assets.state.load_texture(.{
.format = .rgba8,
.access = .{
@ -30,7 +30,7 @@ fn load(display: ona.Write(gfx.Display), actors: ona.Write(Effects), assets: ona
},
});
actors.state.crt_effect = try assets.state.load_effect_file(ona.files.bundle, "./crt.frag.spv");
effects.state.crt_effect = try assets.state.load_effect_file(ona.files.bundle, "./crt.frag.spv");
}
pub const main = ona.App.setup.

129
demos/inputs.zig Normal file
View File

@ -0,0 +1,129 @@
const gfx = @import("gfx");
const hid = @import("hid");
const ona = @import("ona");
const std = @import("std");
const Spawned = struct {
transform: gfx.Transform2D,
lifetime_seconds: f32,
};
const Visuals = struct {
spawned_keyboards: ona.stack.Parallel(Spawned) = .{},
spawned_mouses: ona.stack.Parallel(Spawned) = .{},
keyboard_icon: gfx.Texture = .default,
mouse_icon: gfx.Texture = .default,
random: std.Random.Xoroshiro128,
mouse_position: @Vector(2, f32) = @splat(0),
};
fn cleanup(visuals: ona.Write(Visuals)) !void {
visuals.state.spawned_keyboards.deinit();
visuals.state.spawned_mouses.deinit();
}
fn load(visuals: ona.Write(Visuals)) !void {
const initial_spawn_capacity = 1024;
try visuals.state.spawned_keyboards.grow(initial_spawn_capacity);
try visuals.state.spawned_mouses.grow(initial_spawn_capacity);
}
pub const main = ona.App.setup.
with_module(gfx).
with_module(hid).
with_state(Visuals{.random = std.Random.Xoroshiro128.init(47342563891212)}).
with_system(.load, ona.system_fn(load), .{.label = "load visuals"}).
with_system(.update, ona.system_fn(update), .{.label = "spawn visuals"}).
with_system(.render, ona.system_fn(render), .{.label = "render visuals"}).
with_system(.exit, ona.system_fn(cleanup), .{.label = "clean up visuals"}).build();
fn update(visuals: ona.Write(Visuals), app: ona.Read(ona.App), events: ona.Receive(hid.Event), display: ona.Read(gfx.Display)) !void {
update_spawned(&visuals.state.spawned_keyboards, @floatCast(app.state.delta_time));
update_spawned(&visuals.state.spawned_mouses, @floatCast(app.state.delta_time));
const random = visuals.state.random.random();
const width: f32 = @floatFromInt(display.state.width);
const height: f32 = @floatFromInt(display.state.height);
const icon_scale = .{64, 64};
for (events.messages()) |event| {
switch (event) {
.key_down => {
try visuals.state.spawned_keyboards.push_grow(.{
.lifetime_seconds = 2.5 + (5 * random.float(f32)),
.transform = gfx.Transform2D.from_simplified(.{
.translation = .{width * random.float(f32), height},
.rotation = std.math.pi * random.float(f32),
.scale = icon_scale,
}),
});
},
.mouse_down => {
try visuals.state.spawned_mouses.push_grow(.{
.lifetime_seconds = 2.5 + (5 * random.float(f32)),
.transform = gfx.Transform2D.from_simplified(.{
.translation = visuals.state.mouse_position,
.rotation = std.math.pi * random.float(f32),
.scale = icon_scale,
}),
});
},
.mouse_motion => |motion| {
visuals.state.mouse_position = motion.absolute_position;
},
else => {}
}
}
}
fn update_spawned(spawned: *ona.stack.Parallel(Spawned), delta_time: f32) void {
const float_speed = 50;
for (spawned.values.slice(.transform)) |*transform| {
transform.* = transform.translated(.{0, float_speed * -delta_time});
}
{
var range = spawned.len();
var index: usize = 0;
while (index < range) : (index += 1) {
const lifetime_seconds = spawned.values.get(.lifetime_seconds, index).?;
lifetime_seconds.* -= delta_time;
if (lifetime_seconds.* <= 0) {
range -= 1;
std.mem.swap(f32, lifetime_seconds, spawned.values.get(.lifetime_seconds, range).?);
}
}
std.debug.assert(spawned.pop_many(spawned.len() - range));
}
}
fn render(visuals: ona.Write(Visuals), commands: gfx.Commands) !void {
for (visuals.state.spawned_keyboards.values.slice(.transform)) |transform| {
try commands.draw_texture(.{
.texture = visuals.state.keyboard_icon,
.transform = transform,
});
}
for (visuals.state.spawned_mouses.values.slice(.transform)) |transform| {
try commands.draw_texture(.{
.texture = visuals.state.keyboard_icon,
.transform = transform,
});
}
}

View File

@ -1,6 +1,6 @@
pub const colors = @import("./colors.zig");
const input = @import("input");
const hid = @import("hid");
const ona = @import("ona");
@ -349,7 +349,32 @@ pub const Transform2D = extern struct {
ybasis: Vector = .{0, 1},
origin: Vector = @splat(0),
const Vector = @Vector(2, f32);
pub const Simplified = struct {
translation: Vector = @splat(0),
rotation: f32 = 0,
scale: Vector = @splat(1),
skew: f32 = 0,
};
pub const Vector = @Vector(2, f32);
pub fn from_simplified(simplified: Simplified) Transform2D {
const rotation_skew = simplified.rotation + simplified.skew;
return .{
.xbasis = simplified.scale * Vector{std.math.cos(simplified.rotation), std.math.sin(simplified.rotation)},
.ybasis = simplified.scale * Vector{-std.math.sin(rotation_skew), std.math.cos(rotation_skew)},
.origin = simplified.translation,
};
}
pub fn translated(self: Transform2D, translation: Vector) Transform2D {
var transform = self;
transform.origin += translation;
return transform;
}
};
fn load_bmp_texture(arena: *std.heap.ArenaAllocator, storage: ona.files.Storage, path: []const u8) !Texture.Desc {
@ -426,14 +451,54 @@ fn load_bmp_texture(arena: *std.heap.ArenaAllocator, storage: ona.files.Storage,
};
}
pub fn poll(app: ona.Write(ona.App), events: ona.Send(input.Event)) !void {
pub fn poll(app: ona.Write(ona.App), events: ona.Send(hid.Event)) !void {
var event = @as(ext.SDL_Event, undefined);
while (ext.SDL_PollEvent(&event) != 0) {
switch (event.type) {
ext.SDL_QUIT => app.state.quit(),
ext.SDL_KEYUP => try events.push(.{.key_up = @enumFromInt(event.key.keysym.scancode)}),
ext.SDL_KEYDOWN => try events.push(.{.key_down = @enumFromInt(event.key.keysym.scancode)}),
ext.SDL_QUIT => {
app.state.quit();
},
ext.SDL_KEYUP => {
try events.push(.{.key_up = @enumFromInt(event.key.keysym.scancode)});
},
ext.SDL_KEYDOWN => {
try events.push(.{.key_down = @enumFromInt(event.key.keysym.scancode)});
},
ext.SDL_MOUSEBUTTONUP => {
try events.push(.{
.mouse_up = switch (event.button.button) {
ext.SDL_BUTTON_LEFT => .left,
ext.SDL_BUTTON_RIGHT => .right,
ext.SDL_BUTTON_MIDDLE => .middle,
else => unreachable,
},
});
},
ext.SDL_MOUSEBUTTONDOWN => {
try events.push(.{
.mouse_down = switch (event.button.button) {
ext.SDL_BUTTON_LEFT => .left,
ext.SDL_BUTTON_RIGHT => .right,
ext.SDL_BUTTON_MIDDLE => .middle,
else => unreachable,
},
});
},
ext.SDL_MOUSEMOTION => {
try events.push(.{
.mouse_motion = .{
.relative_position = .{@floatFromInt(event.motion.xrel), @floatFromInt(event.motion.yrel)},
.absolute_position = .{@floatFromInt(event.motion.x), @floatFromInt(event.motion.y)},
},
});
},
else => {},
}
}

View File

@ -94,7 +94,7 @@ const Frame = struct {
try self.texture_batch_buffers.push_grow(instance_buffer);
}
_ = sokol.gfx.appendBuffer(self.texture_batch_buffers.get().?, sokol.gfx.asRange(&DrawTexture{
_ = sokol.gfx.appendBuffer(self.texture_batch_buffers.get().?.*, sokol.gfx.asRange(&DrawTexture{
.transform = command.transform,
}));

View File

@ -3,15 +3,19 @@ const ona = @import("ona");
const std = @import("std");
pub const Axis = struct {
keys: ?[2]Key = null,
has_key_scancodes: ?[2]KeyScancode = null,
has_mouse_buttons: ?[2]MouseButton = null,
};
pub const Event = union (enum) {
key_up: Key,
key_down: Key,
key_up: KeyScancode,
key_down: KeyScancode,
mouse_up: MouseButton,
mouse_down: MouseButton,
mouse_motion: MouseMotion,
};
pub const Key = enum (u32) {
pub const KeyScancode = enum (u32) {
no_event = 0x00,
error_rollover = 0x01,
post_fail = 0x02,
@ -189,21 +193,49 @@ pub const Key = enum (u32) {
};
pub const Mapping = struct {
keys_pressed: ScancodeSet = ScancodeSet.initEmpty(),
keys_held: ScancodeSet = ScancodeSet.initEmpty(),
key_scancodes: ActionSets(512) = .{},
mouse_buttons: ActionSets(std.enums.values(MouseButton).len) = .{},
mouse_position: MousePosition = @splat(0),
const ScancodeSet = std.bit_set.StaticBitSet(512);
fn ActionSets(comptime len: usize) type {
const Set = std.bit_set.StaticBitSet(len);
return struct {
released: Set = Set.initEmpty(),
pressed: Set = Set.initEmpty(),
held: Set = Set.initEmpty(),
const Self = @This();
fn expire_frame(self: *Self) void {
self.pressed = Set.initEmpty();
self.released = Set.initEmpty();
}
};
}
pub fn axis_strength(self: Mapping, axis: Axis) f32 {
if (axis.keys) |keys| {
const key_down, const key_up = keys;
const is_key_down_held = self.keys_held.isSet(@intFromEnum(key_down));
const is_key_up_held = self.keys_held.isSet(@intFromEnum(key_up));
if (axis.has_key_scancodes) |key_scancodes| {
const neg_scancode, const pos_scancode = key_scancodes;
const is_neg_held = self.key_scancodes.held.isSet(@intFromEnum(neg_scancode));
const is_pos_held = self.key_scancodes.held.isSet(@intFromEnum(pos_scancode));
if (is_key_down_held or is_key_up_held) {
if (is_neg_held or is_pos_held) {
return
@as(f32, @floatFromInt(@intFromBool(is_key_up_held))) -
@as(f32, @floatFromInt(@intFromBool(is_key_down_held)));
@as(f32, @floatFromInt(@intFromBool(is_pos_held))) -
@as(f32, @floatFromInt(@intFromBool(is_neg_held)));
}
}
if (axis.has_mouse_buttons) |mouse_buttons| {
const neg_button, const pos_button = mouse_buttons;
const is_neg_held = self.mouse_buttons.held.isSet(@intFromEnum(neg_button));
const is_pos_held = self.mouse_buttons.held.isSet(@intFromEnum(pos_button));
if (is_neg_held or is_pos_held) {
return
@as(f32, @floatFromInt(@intFromBool(is_pos_held))) -
@as(f32, @floatFromInt(@intFromBool(is_neg_held)));
}
}
@ -211,9 +243,22 @@ pub const Mapping = struct {
}
};
pub const MouseButton = enum {
left,
right,
middle,
};
pub const MouseMotion = struct {
relative_position: MousePosition,
absolute_position: MousePosition,
};
pub const MousePosition = @Vector(2, f32);
test "mapping values" {
const axis = Axis{
.keys = .{.minus, .equal},
.has_key_scancodes = .{.minus, .equal},
};
{
@ -225,7 +270,7 @@ test "mapping values" {
{
var mapping = Mapping{};
mapping.keys_held.set(@intFromEnum(Key.equal));
mapping.key_scancodes.held.set(@intFromEnum(KeyScancode.equal));
try std.testing.expectEqual(mapping.axis_strength(axis), 1);
}
@ -233,7 +278,7 @@ test "mapping values" {
{
var mapping = Mapping{};
mapping.keys_held.set(@intFromEnum(Key.minus));
mapping.key_scancodes.held.set(@intFromEnum(KeyScancode.minus));
try std.testing.expectEqual(mapping.axis_strength(axis), -1);
}
@ -241,8 +286,8 @@ test "mapping values" {
{
var mapping = Mapping{};
mapping.keys_held.set(@intFromEnum(Key.minus));
mapping.keys_held.set(@intFromEnum(Key.equal));
mapping.key_scancodes.held.set(@intFromEnum(KeyScancode.minus));
mapping.key_scancodes.held.set(@intFromEnum(KeyScancode.equal));
try std.testing.expectEqual(mapping.axis_strength(axis), 0);
}
@ -256,18 +301,34 @@ pub fn setup(world: *ona.World, events: ona.App.Events) std.mem.Allocator.Error!
});
}
pub fn update(inputs: ona.msg.Receive(ona.gfx.Input), mapping: ona.Write(Mapping)) void {
mapping.state.keys_pressed = Mapping.ScancodeSet.initEmpty();
pub fn update(inputs: ona.Receive(Event), mapping: ona.Write(Mapping)) void {
mapping.state.key_scancodes.expire_frame();
mapping.state.mouse_buttons.expire_frame();
for (inputs.messages()) |message| {
switch (message) {
.key_down => |key| {
mapping.state.keys_pressed.set(key.scancode());
mapping.state.keys_held.set(key.scancode());
.key_down => |scancode| {
mapping.state.key_scancodes.pressed.set(@intFromEnum(scancode));
mapping.state.key_scancodes.held.set(@intFromEnum(scancode));
},
.key_up => |key| {
mapping.state.keys_held.unset(key.scancode());
.key_up => |scancode| {
mapping.state.key_scancodes.held.unset(@intFromEnum(scancode));
mapping.state.key_scancodes.released.set(@intFromEnum(scancode));
},
.mouse_down => |button| {
mapping.state.mouse_buttons.pressed.unset(@intFromEnum(button));
mapping.state.mouse_buttons.held.set(@intFromEnum(button));
},
.mouse_up => |button| {
mapping.state.key_scancodes.held.unset(@intFromEnum(button));
mapping.state.key_scancodes.released.set(@intFromEnum(button));
},
.mouse_motion => |motion| {
mapping.state.mouse_position = motion.absolute_position;
},
}
}

View File

@ -14,7 +14,7 @@ pub const BindContext = struct {
world: *Self,
pub fn accesses_state(self: BindContext, access: std.meta.Tag(StateAccess), id: ona.TypeID) bool {
const resource_accesses = &self.systems.graph.get_ptr(self.node).?.resource_accesses;
const resource_accesses = &self.systems.graph.get(self.node).?.resource_accesses;
for (resource_accesses.values) |resource_access| {
switch (resource_access) {
@ -43,7 +43,7 @@ pub const BindContext = struct {
const id = ona.type_id(Resource);
if (!self.accesses_state(.read_write, id)) {
try self.systems.graph.get_ptr(self.node).?.resource_accesses.push_grow(.{.read_write = id});
try self.systems.graph.get(self.node).?.resource_accesses.push_grow(.{.read_write = id});
}
const read_write_resource_nodes = lazily_create: {
@ -71,7 +71,7 @@ pub const BindContext = struct {
const id = ona.type_id(Resource);
if (!self.accesses_state(.read_only, id)) {
try self.systems.graph.get_ptr(self.node).?.resource_accesses.push_grow(.{.read_only = id});
try self.systems.graph.get(self.node).?.resource_accesses.push_grow(.{.read_only = id});
}
const read_only_resource_nodes = lazily_create: {
@ -167,7 +167,7 @@ const Schedule = struct {
var nodes = self.graph.nodes();
while (nodes.next()) |node| {
const system = self.graph.get_ptr(node).?;
const system = self.graph.get(node).?;
for (system.info.used_parameters(), system.parameter_states[0 .. system.info.parameter_count]) |parameter, state| {
parameter.unbind(self.arena.allocator(), state, .{
@ -250,7 +250,7 @@ const Schedule = struct {
.resource_accesses = .{.allocator = ona.heap.allocator},
});
const system_node = schedule.graph.get_ptr(node).?;
const system_node = schedule.graph.get(node).?;
const system_parameter_states = system_node.parameter_states[0 .. system.info.parameter_count];
errdefer {
@ -281,7 +281,7 @@ const Schedule = struct {
var nodes = schedule.graph.nodes();
while (nodes.next()) |node| {
const system = schedule.graph.get_ptr(node).?;
const system = schedule.graph.get(node).?;
for (system.dependencies) |order| {
const dependencies = schedule.system_id_nodes.get(@intFromPtr(system.info)) orelse {
@ -363,7 +363,7 @@ const Schedule = struct {
try schedule.parallel_work_bundles.push_grow(.{.allocator = ona.heap.allocator});
const bundle = schedule.parallel_work_bundles.get_ptr().?;
const bundle = schedule.parallel_work_bundles.get().?;
errdefer {
bundle.deinit();
@ -379,7 +379,7 @@ const Schedule = struct {
while (index < work.len()) : (index += 1) {
const node = work.values[index];
switch (schedule.graph.get_ptr(node).?.info.thread_restriction) {
switch (schedule.graph.get(node).?.info.thread_restriction) {
.none => continue,
.main => {
@ -408,7 +408,7 @@ const Schedule = struct {
defer work_group.finish();
for (bundle.values) |node| {
const system = graph.get_ptr(node).?;
const system = graph.get(node).?;
// TODO: std lib thread pool sucks for many reasons and this is one of them.
system.info.execute(system.info.used_parameters(), &system.parameter_states) catch unreachable;
@ -428,7 +428,7 @@ const Schedule = struct {
} else {
for (self.parallel_work_bundles.values) |bundle| {
for (bundle.values) |node| {
const system = self.graph.get_ptr(node).?;
const system = self.graph.get(node).?;
try system.info.execute(system.info.used_parameters(), &system.parameter_states);
}
@ -436,7 +436,7 @@ const Schedule = struct {
}
for (self.blocking_work.values) |node| {
const system = self.graph.get_ptr(node).?;
const system = self.graph.get(node).?;
try system.info.execute(system.info.used_parameters(), &system.parameter_states);
}

View File

@ -50,19 +50,23 @@ pub fn Graph(comptime Payload: type) type {
return null;
}
return self.table.values.get_ptr(.edges, @intFromEnum(node)).?.values;
return self.table.values.get(.edges, @intFromEnum(node)).?.values;
}
pub fn exists(self: Self, node: Node) bool {
return self.table.values.get(.is_occupied, @intFromEnum(node)) orelse false;
if (self.table.values.get(.is_occupied, @intFromEnum(node))) |is_occupied| {
return is_occupied.*;
}
pub fn get_ptr(self: Self, node: Node) ?*Payload {
return false;
}
pub fn get(self: Self, node: Node) ?*Payload {
if (!self.exists(node)) {
return null;
}
return self.table.values.get_ptr(.payload, @intFromEnum(node)).?;
return self.table.values.get(.payload, @intFromEnum(node)).?;
}
pub fn init(allocator: std.mem.Allocator) Self {
@ -76,7 +80,7 @@ pub fn Graph(comptime Payload: type) type {
return false;
}
const edges = self.table.values.get_ptr(.edges, @intFromEnum(dependant_node)) orelse {
const edges = self.table.values.get(.edges, @intFromEnum(dependant_node)) orelse {
return false;
};
@ -114,10 +118,10 @@ pub fn Graph(comptime Payload: type) type {
const node_index = @intFromEnum(node);
self.table.values.get_ptr(.is_occupied, node_index).?.* = false;
self.table.values.get(.is_occupied, node_index).?.* = false;
self.node_count -= 1;
return self.table.values.get(.payload, node_index).?;
return self.table.values.get(.payload, node_index).?.*;
}
pub fn reset_visited(self: *Self) void {
@ -129,7 +133,7 @@ pub fn Graph(comptime Payload: type) type {
return null;
}
return self.table.values.get(.is_visited, @intFromEnum(node)).?;
return self.table.values.get(.is_visited, @intFromEnum(node)).?.*;
}
};
}

View File

@ -32,6 +32,7 @@ pub const App = struct {
events: *const Events,
target_frame_time: f64,
elapsed_time: f64,
delta_time: f64,
is_running: bool,
pub const Events = struct {
@ -114,6 +115,7 @@ pub const App = struct {
.events = &events,
.target_frame_time = 1.0 / 60.0,
.elapsed_time = 0,
.delta_time = 0,
.is_running = true,
});
@ -127,11 +129,11 @@ pub const App = struct {
while (app.is_running) {
const ticks_current = std.time.milliTimestamp();
const milliseconds_per_second = 1000.0;
const delta_time = @as(f64, @floatFromInt(ticks_current - ticks_previous)) / milliseconds_per_second;
app.delta_time = @as(f64, @floatFromInt(ticks_current - ticks_previous)) / milliseconds_per_second;
app.elapsed_time = @as(f64, @floatFromInt(ticks_current - ticks_initial)) / milliseconds_per_second;
ticks_previous = ticks_current;
accumulated_time += delta_time;
accumulated_time += app.delta_time;
try world.run_event(events.pre_update);
@ -519,6 +521,18 @@ pub fn Receive(comptime Message: type) type {
.channel = (try context.register_readable_state_access(TypedChannel)) orelse set: {
try context.world.set_state(TypedChannel.init(heap.allocator));
const app = context.world.get_state(App) orelse {
@panic("Send system parameters depend on a " ++ @typeName(App) ++ " state to work");
};
try context.world.on_event(app.events.post_update, system_fn(TypedChannel.swap), .{
.label = "swap channel of " ++ @typeName(Message),
});
try context.world.on_event(app.events.exit, system_fn(TypedChannel.cleanup), .{
.label = "clean up channel of " ++ @typeName(Message),
});
break: set (try context.register_readable_state_access(TypedChannel)).?;
},
};

View File

@ -57,15 +57,7 @@ pub fn Parallel(comptime Type: type) type {
return sliced;
}
pub fn get(self: Self, comptime field: Field, index: usize) ?Element(field) {
if (index >= self.len) {
return null;
}
return self.ptr(field)[index];
}
pub fn get_ptr(self: Self, comptime field: Field, index: usize) ?*Element(field) {
pub fn get(self: Self, comptime field: Field, index: usize) ?*Element(field) {
if (index >= self.len) {
return null;
}
@ -105,14 +97,6 @@ pub fn get(slice: anytype, index: usize) ?@typeInfo(@TypeOf(slice)).Pointer.chil
return slice[index];
}
pub fn get_ptr(slice: anytype, index: usize) ?ElementPtr(@TypeOf(slice)) {
if (index >= slice.len) {
return null;
}
return &slice[index];
}
pub fn parallel_alloc(comptime Element: type, allocator: std.mem.Allocator, n: usize) std.mem.Allocator.Error!Parallel(Element) {
const alignment = @alignOf(Element);
const Slices = Parallel(Element);

View File

@ -47,15 +47,7 @@ pub fn Sequential(comptime Value: type) type {
return self.values.len == 0;
}
pub fn get(self: Self) ?Value {
if (self.get_ptr()) |value| {
return value.*;
}
return null;
}
pub fn get_ptr(self: Self) ?*Value {
pub fn get(self: Self) ?*Value {
if (self.values.len == 0) {
return null;
}
@ -208,7 +200,7 @@ pub fn Parallel(comptime Value: type) type {
const alignment = @alignOf(Value);
return struct {
allocator: std.mem.Allocator,
allocator: std.mem.Allocator = ona.heap.allocator,
values: Slices = .{},
cap: usize = 0,
@ -230,7 +222,7 @@ pub fn Parallel(comptime Value: type) type {
self.* = undefined;
}
pub fn get_ptr(self: Self, comptime field: Slices.Field) ?*align (alignment) Slices.Element(field) {
pub fn get(self: Self, comptime field: Slices.Field) ?*align (alignment) Slices.Element(field) {
if (self.len() == 0) {
return null;
}
@ -255,6 +247,16 @@ pub fn Parallel(comptime Value: type) type {
return self.values.len;
}
pub fn pop_many(self: *Self, n: usize) bool {
const new_length = ona.scalars.sub(self.values.len, n) orelse {
return false;
};
self.values = self.values.slice_all(0, new_length).?;
return true;
}
pub fn push_grow(self: *Self, value: Value) std.mem.Allocator.Error!void {
if (self.len() == self.cap) {
try self.grow(@max(1, self.cap));