Move all the allocators to a separate file. They can now be imported with main.heap (analogous to std.heap)

I also removed the unused BufferFallbackAllocator which was from a time where I was still experimenting with different allocators.
This commit is contained in:
IntegratedQuantum 2025-03-09 16:23:52 +01:00
parent 3c7231cb43
commit 09fc516828
48 changed files with 571 additions and 614 deletions

View File

@ -7,7 +7,7 @@ const Item = main.items.Item;
const ItemStack = main.items.ItemStack;
const Tool = main.items.Tool;
const utils = main.utils;
const NeverFailingAllocator = utils.NeverFailingAllocator;
const NeverFailingAllocator = main.heap.NeverFailingAllocator;
const vec = main.vec;
const Vec3d = vec.Vec3d;
const Vec3f = vec.Vec3f;

View File

@ -6,9 +6,9 @@ const migrations_zig = @import("migrations.zig");
const ZonElement = @import("zon.zig").ZonElement;
const main = @import("main.zig");
const biomes_zig = main.server.terrain.biomes;
const NeverFailingAllocator = main.utils.NeverFailingAllocator;
const NeverFailingAllocator = main.heap.NeverFailingAllocator;
var arena: main.utils.NeverFailingArenaAllocator = undefined;
var arena: main.heap.NeverFailingArenaAllocator = undefined;
var arenaAllocator: NeverFailingAllocator = undefined;
var commonBlocks: std.StringHashMap(ZonElement) = undefined;
var commonBlockMigrations: std.StringHashMap(ZonElement) = undefined;
@ -92,7 +92,7 @@ pub fn readAllZonFilesInAddons(
};
defer dir.close();
var defaultsArena: main.utils.NeverFailingArenaAllocator = .init(main.stackAllocator);
var defaultsArena: main.heap.NeverFailingArenaAllocator = .init(main.stackAllocator);
defer defaultsArena.deinit();
const defaultsArenaAllocator = defaultsArena.allocator();

View File

@ -43,7 +43,7 @@ pub const BlockTag = enum(u32) {
return result;
}
pub fn loadFromZon(_allocator: main.utils.NeverFailingAllocator, zon: ZonElement) []BlockTag {
pub fn loadFromZon(_allocator: main.heap.NeverFailingAllocator, zon: ZonElement) []BlockTag {
const result = _allocator.alloc(BlockTag, zon.toSlice().len);
for(zon.toSlice(), 0..) |tagZon, i| {
result[i] = BlockTag.find(tagZon.as([]const u8, "incorrect"));
@ -56,7 +56,7 @@ pub const BlockTag = enum(u32) {
}
};
var arena = main.utils.NeverFailingArenaAllocator.init(main.globalAllocator);
var arena = main.heap.NeverFailingArenaAllocator.init(main.globalAllocator);
const allocator = arena.allocator();
pub const maxBlockCount: usize = 65536; // 16 bit limit
@ -447,7 +447,7 @@ pub const meshes = struct { // MARK: meshes
var textureFogData: main.List(FogData) = undefined;
pub var textureOcclusionData: main.List(bool) = undefined;
var arenaForWorld: main.utils.NeverFailingArenaAllocator = undefined;
var arenaForWorld: main.heap.NeverFailingArenaAllocator = undefined;
pub var blockBreakingTextures: main.List(u16) = undefined;
@ -569,7 +569,7 @@ pub const meshes = struct { // MARK: meshes
}
}
fn extendedPath(_allocator: main.utils.NeverFailingAllocator, path: []const u8, ending: []const u8) []const u8 {
fn extendedPath(_allocator: main.heap.NeverFailingAllocator, path: []const u8, ending: []const u8) []const u8 {
return std.fmt.allocPrint(_allocator.allocator, "{s}{s}", .{path, ending}) catch unreachable;
}

View File

@ -14,7 +14,7 @@ const Mat4f = vec.Mat4f;
const Vec3d = vec.Vec3d;
const Vec3f = vec.Vec3f;
const Vec4f = vec.Vec4f;
const NeverFailingAllocator = main.utils.NeverFailingAllocator;
const NeverFailingAllocator = main.heap.NeverFailingAllocator;
const BinaryReader = main.utils.BinaryReader;

View File

@ -2,7 +2,7 @@ const std = @import("std");
const builtin = @import("builtin");
const main = @import("root");
const NeverFailingAllocator = main.utils.NeverFailingAllocator;
const NeverFailingAllocator = main.heap.NeverFailingAllocator;
const ZonElement = main.ZonElement;
pub fn read(allocator: NeverFailingAllocator, path: []const u8) ![]u8 {

View File

@ -27,7 +27,7 @@ const Vec3f = vec.Vec3f;
const main = @import("main.zig");
const Window = main.Window;
const NeverFailingAllocator = main.utils.NeverFailingAllocator;
const NeverFailingAllocator = main.heap.NeverFailingAllocator;
pub const c = @cImport({
@cInclude("glad/glad.h");
@ -1698,7 +1698,7 @@ pub const TextureArray = struct { // MARK: TextureArray
for(0..maxLOD) |i| {
c.glTexImage3D(c.GL_TEXTURE_2D_ARRAY, @intCast(i), c.GL_RGBA8, @max(0, maxWidth >> @intCast(i)), @max(0, maxHeight >> @intCast(i)), @intCast(images.len), 0, c.GL_RGBA, c.GL_UNSIGNED_BYTE, null);
}
var arena = main.utils.NeverFailingArenaAllocator.init(main.stackAllocator);
var arena = main.heap.NeverFailingArenaAllocator.init(main.stackAllocator);
defer arena.deinit();
const lodBuffer: [][]Color = arena.allocator().alloc([]Color, maxLOD);
for(lodBuffer, 0..) |*buffer, i| {

View File

@ -9,7 +9,7 @@ const Texture = graphics.Texture;
const random = main.random;
const vec = main.vec;
const Vec2f = vec.Vec2f;
const NeverFailingAllocator = main.utils.NeverFailingAllocator;
const NeverFailingAllocator = main.heap.NeverFailingAllocator;
const gui = @import("../gui.zig");
const GuiComponent = gui.GuiComponent;

View File

@ -9,7 +9,7 @@ const vec = main.vec;
const Vec2f = vec.Vec2f;
const List = main.List;
const NeverFailingAllocator = main.utils.NeverFailingAllocator;
const NeverFailingAllocator = main.heap.NeverFailingAllocator;
const Button = @import("components/Button.zig");
const CheckBox = @import("components/CheckBox.zig");

View File

@ -24,7 +24,7 @@ fn delayCallback(newValue: f32) void {
settings.save();
}
fn delayFormatter(allocator: main.utils.NeverFailingAllocator, value: f32) []const u8 {
fn delayFormatter(allocator: main.heap.NeverFailingAllocator, value: f32) []const u8 {
return std.fmt.allocPrint(allocator.allocator, "#ffffffPlace/Break Delay: {d:.0} ms", .{value}) catch unreachable;
}
@ -33,7 +33,7 @@ fn speedCallback(newValue: f32) void {
settings.save();
}
fn speedFormatter(allocator: main.utils.NeverFailingAllocator, value: f32) []const u8 {
fn speedFormatter(allocator: main.heap.NeverFailingAllocator, value: f32) []const u8 {
return std.fmt.allocPrint(allocator.allocator, "#ffffffPlace/Break Speed: {d:.0} ms", .{value}) catch unreachable;
}

View File

@ -59,11 +59,11 @@ fn updateDeadzone(deadzone: f32) void {
main.settings.controllerAxisDeadzone = deadzone;
}
fn deadzoneFormatter(allocator: main.utils.NeverFailingAllocator, value: f32) []const u8 {
fn deadzoneFormatter(allocator: main.heap.NeverFailingAllocator, value: f32) []const u8 {
return std.fmt.allocPrint(allocator.allocator, "Deadzone: {d:.0}%", .{value*100}) catch unreachable;
}
fn sensitivityFormatter(allocator: main.utils.NeverFailingAllocator, value: f32) []const u8 {
fn sensitivityFormatter(allocator: main.heap.NeverFailingAllocator, value: f32) []const u8 {
return std.fmt.allocPrint(allocator.allocator, "{s} Sensitivity: {d:.0}%", .{if(editingKeyboard) "Mouse" else "Controller", value*100}) catch unreachable;
}

View File

@ -37,7 +37,7 @@ fn fpsCapRound(newValue: f32) ?u32 {
}
}
fn fpsCapFormatter(allocator: main.utils.NeverFailingAllocator, value: f32) []const u8 {
fn fpsCapFormatter(allocator: main.heap.NeverFailingAllocator, value: f32) []const u8 {
const cap = fpsCapRound(value);
if(cap == null)
return allocator.dupe(u8, "#ffffffFPS: Unlimited");
@ -70,11 +70,11 @@ fn fovCallback(newValue: f32) void {
main.Window.GLFWCallbacks.framebufferSize(undefined, main.Window.width, main.Window.height);
}
fn fovFormatter(allocator: main.utils.NeverFailingAllocator, value: f32) []const u8 {
fn fovFormatter(allocator: main.heap.NeverFailingAllocator, value: f32) []const u8 {
return std.fmt.allocPrint(allocator.allocator, "#ffffffField Of View: {d:.0}°", .{value}) catch unreachable;
}
fn lodDistanceFormatter(allocator: main.utils.NeverFailingAllocator, value: f32) []const u8 {
fn lodDistanceFormatter(allocator: main.heap.NeverFailingAllocator, value: f32) []const u8 {
return std.fmt.allocPrint(allocator.allocator, "#ffffffOpaque leaves distance: {d:.0}", .{@round(value)}) catch unreachable;
}

View File

@ -35,7 +35,7 @@ fn discoverIpAddress() void {
}
fn discoverIpAddressFromNewThread() void {
var sta = main.utils.StackAllocator.init(main.globalAllocator, 1 << 23);
var sta = main.heap.StackAllocator.init(main.globalAllocator, 1 << 23);
defer sta.deinit();
main.stackAllocator = sta.allocator();

View File

@ -39,7 +39,7 @@ fn discoverIpAddress() void {
}
fn discoverIpAddressFromNewThread() void {
var sta = main.utils.StackAllocator.init(main.globalAllocator, 1 << 23);
var sta = main.heap.StackAllocator.init(main.globalAllocator, 1 << 23);
defer sta.deinit();
main.stackAllocator = sta.allocator();

View File

@ -4,7 +4,7 @@ const main = @import("root");
const ConnectionManager = main.network.ConnectionManager;
const settings = main.settings;
const Vec2f = main.vec.Vec2f;
const NeverFailingAllocator = main.utils.NeverFailingAllocator;
const NeverFailingAllocator = main.heap.NeverFailingAllocator;
const gui = @import("../gui.zig");
const GuiComponent = gui.GuiComponent;

View File

@ -4,7 +4,7 @@ const main = @import("root");
const ConnectionManager = main.network.ConnectionManager;
const settings = main.settings;
const Vec2f = main.vec.Vec2f;
const NeverFailingAllocator = main.utils.NeverFailingAllocator;
const NeverFailingAllocator = main.heap.NeverFailingAllocator;
const Texture = main.graphics.Texture;
const gui = @import("../gui.zig");
@ -22,7 +22,7 @@ pub var window = GuiWindow{
const padding: f32 = 8;
const width: f32 = 128;
var buttonNameArena: main.utils.NeverFailingArenaAllocator = undefined;
var buttonNameArena: main.heap.NeverFailingArenaAllocator = undefined;
pub var needsUpdate: bool = false;
@ -127,7 +127,7 @@ pub fn update() void {
}
pub fn onOpen() void {
buttonNameArena = main.utils.NeverFailingArenaAllocator.init(main.globalAllocator);
buttonNameArena = main.heap.NeverFailingArenaAllocator.init(main.globalAllocator);
const list = VerticalList.init(.{padding, 16 + padding}, 300, 8);
list.add(Label.init(.{0, 0}, width, "**Select World**", .center));
list.add(Button.initText(.{0, 0}, 128, "Create New World", gui.openWindowCallback("save_creation")));

View File

@ -3,7 +3,7 @@ const std = @import("std");
const main = @import("root");
const settings = main.settings;
const Vec2f = main.vec.Vec2f;
const NeverFailingAllocator = main.utils.NeverFailingAllocator;
const NeverFailingAllocator = main.heap.NeverFailingAllocator;
const gui = @import("../gui.zig");
const GuiComponent = gui.GuiComponent;

View File

@ -20,7 +20,7 @@ const Mat4f = vec.Mat4f;
const Vec3d = vec.Vec3d;
const Vec3f = vec.Vec3f;
const Vec3i = vec.Vec3i;
const NeverFailingAllocator = main.utils.NeverFailingAllocator;
const NeverFailingAllocator = main.heap.NeverFailingAllocator;
const ItemDrop = struct { // MARK: ItemDrop
pos: Vec3d,

View File

@ -14,7 +14,7 @@ const Vec2f = vec.Vec2f;
const Vec2i = vec.Vec2i;
const Vec3i = vec.Vec3i;
const Vec3f = vec.Vec3f;
const NeverFailingAllocator = main.utils.NeverFailingAllocator;
const NeverFailingAllocator = main.heap.NeverFailingAllocator;
const modifierList = @import("tool/modifiers/_list.zig");
@ -747,7 +747,7 @@ pub const Recipe = struct { // MARK: Recipe
cachedInventory: ?Inventory = null,
};
var arena: main.utils.NeverFailingArenaAllocator = undefined;
var arena: main.heap.NeverFailingArenaAllocator = undefined;
var toolTypes: std.StringHashMap(ToolType) = undefined;
var reverseIndices: std.StringHashMap(*BaseItem) = undefined;
var modifiers: std.StringHashMap(*const Modifier.VTable) = undefined;

View File

@ -1,7 +1,7 @@
const std = @import("std");
const main = @import("main.zig");
const NeverFailingAllocator = main.utils.NeverFailingAllocator;
const NeverFailingAllocator = main.heap.NeverFailingAllocator;
const List = main.List;
const JsonType = enum(u8) {
@ -180,7 +180,7 @@ pub const JsonElement = union(JsonType) { // MARK: JsonElement
},
.vector => {
const len = @typeInfo(@TypeOf(value)).vector.len;
const result = initArray(main.utils.NeverFailingAllocator{.allocator = allocator, .IAssertThatTheProvidedAllocatorCantFail = {}});
const result = initArray(main.heap.NeverFailingAllocator{.allocator = allocator, .IAssertThatTheProvidedAllocatorCantFail = {}});
result.JsonArray.ensureCapacity(len);
inline for(0..len) |i| {
result.JsonArray.appendAssumeCapacity(createElementFromRandomType(value[i], allocator));

View File

@ -27,6 +27,8 @@ pub const ZonElement = @import("zon.zig").ZonElement;
pub const Window = @import("graphics/Window.zig");
pub const heap = @import("utils/heap.zig");
pub const List = @import("utils/list.zig").List;
pub const ListUnmanaged = @import("utils/list.zig").ListUnmanaged;
pub const VirtualList = @import("utils/list.zig").VirtualList;
@ -36,11 +38,11 @@ const file_monitor = utils.file_monitor;
const Vec2f = vec.Vec2f;
const Vec3d = vec.Vec3d;
pub threadlocal var stackAllocator: utils.NeverFailingAllocator = undefined;
pub threadlocal var stackAllocator: heap.NeverFailingAllocator = undefined;
pub threadlocal var seed: u64 = undefined;
var global_gpa = std.heap.GeneralPurposeAllocator(.{.thread_safe = true}){};
var handled_gpa = utils.ErrorHandlingAllocator.init(global_gpa.allocator());
pub const globalAllocator: utils.NeverFailingAllocator = handled_gpa.allocator();
var handled_gpa = heap.ErrorHandlingAllocator.init(global_gpa.allocator());
pub const globalAllocator: heap.NeverFailingAllocator = handled_gpa.allocator();
pub var threadPool: *utils.ThreadPool = undefined;
fn cacheStringImpl(comptime len: usize, comptime str: [len]u8) []const u8 {
@ -522,7 +524,7 @@ pub fn main() void { // MARK: main()
defer if(global_gpa.deinit() == .leak) {
std.log.err("Memory leak", .{});
};
var sta = utils.StackAllocator.init(globalAllocator, 1 << 23);
var sta = heap.StackAllocator.init(globalAllocator, 1 << 23);
defer sta.deinit();
stackAllocator = sta.allocator();

View File

@ -4,7 +4,7 @@ const main = @import("main.zig");
const ZonElement = @import("zon.zig").ZonElement;
const Palette = @import("assets.zig").Palette;
var arenaAllocator = main.utils.NeverFailingArenaAllocator.init(main.globalAllocator);
var arenaAllocator = main.heap.NeverFailingArenaAllocator.init(main.globalAllocator);
const migrationAllocator = arenaAllocator.allocator();
var blockMigrations: std.StringHashMap([]const u8) = .init(migrationAllocator.allocator);

View File

@ -10,7 +10,7 @@ const Vec3f = vec.Vec3f;
const Vec2f = vec.Vec2f;
const Mat4f = vec.Mat4f;
const FaceData = main.renderer.chunk_meshing.FaceData;
const NeverFailingAllocator = main.utils.NeverFailingAllocator;
const NeverFailingAllocator = main.heap.NeverFailingAllocator;
var quadSSBO: graphics.SSBO = undefined;
@ -199,7 +199,7 @@ pub const Model = struct {
return Model.init(quadInfos);
}
pub fn loadRawModelDataFromObj(allocator: main.utils.NeverFailingAllocator, data: []const u8) []QuadInfo {
pub fn loadRawModelDataFromObj(allocator: main.heap.NeverFailingAllocator, data: []const u8) []QuadInfo {
var vertices = main.List(Vec3f).init(main.stackAllocator);
defer vertices.deinit();

View File

@ -19,7 +19,7 @@ const vec = @import("vec.zig");
const Vec3d = vec.Vec3d;
const Vec3f = vec.Vec3f;
const Vec3i = vec.Vec3i;
const NeverFailingAllocator = main.utils.NeverFailingAllocator;
const NeverFailingAllocator = main.heap.NeverFailingAllocator;
pub const networkEndian: std.builtin.Endian = .big;
@ -559,7 +559,7 @@ pub const ConnectionManager = struct { // MARK: ConnectionManager
pub fn run(self: *ConnectionManager) void {
self.threadId = std.Thread.getCurrentId();
var sta = utils.StackAllocator.init(main.globalAllocator, 1 << 23);
var sta = main.heap.StackAllocator.init(main.globalAllocator, 1 << 23);
defer sta.deinit();
main.stackAllocator = sta.allocator();

View File

@ -5,7 +5,7 @@ const ZonElement = main.ZonElement;
const vec = main.vec;
const Vec3f = vec.Vec3f;
const Vec3d = vec.Vec3d;
const NeverFailingAllocator = main.utils.NeverFailingAllocator;
const NeverFailingAllocator = main.heap.NeverFailingAllocator;
pos: Vec3d = .{0, 0, 0},
vel: Vec3d = .{0, 0, 0},

View File

@ -293,7 +293,7 @@ fn deinit() void {
command.deinit();
}
pub fn getUserListAndIncreaseRefCount(allocator: utils.NeverFailingAllocator) []*User {
pub fn getUserListAndIncreaseRefCount(allocator: main.heap.NeverFailingAllocator) []*User {
userMutex.lock();
defer userMutex.unlock();
const result = allocator.dupe(*User, users.items);
@ -303,14 +303,14 @@ pub fn getUserListAndIncreaseRefCount(allocator: utils.NeverFailingAllocator) []
return result;
}
pub fn freeUserListAndDecreaseRefCount(allocator: utils.NeverFailingAllocator, list: []*User) void {
pub fn freeUserListAndDecreaseRefCount(allocator: main.heap.NeverFailingAllocator, list: []*User) void {
for(list) |user| {
user.decreaseRefCount();
}
allocator.free(list);
}
fn getInitialEntityList(allocator: utils.NeverFailingAllocator) []const u8 {
fn getInitialEntityList(allocator: main.heap.NeverFailingAllocator) []const u8 {
// Send the entity updates:
var initialList: []const u8 = undefined;
const list = main.ZonElement.initArray(main.stackAllocator);
@ -369,7 +369,7 @@ fn update() void { // MARK: update()
}
pub fn start(name: []const u8, port: ?u16) void {
var sta = utils.StackAllocator.init(main.globalAllocator, 1 << 23);
var sta = main.heap.StackAllocator.init(main.globalAllocator, 1 << 23);
defer sta.deinit();
main.stackAllocator = sta.allocator();
std.debug.assert(!running.load(.monotonic)); // There can only be one server.

View File

@ -170,7 +170,7 @@ pub const RegionFile = struct { // MARK: RegionFile
}
}
pub fn getChunk(self: *RegionFile, allocator: main.utils.NeverFailingAllocator, relX: usize, relY: usize, relZ: usize) ?[]const u8 {
pub fn getChunk(self: *RegionFile, allocator: main.heap.NeverFailingAllocator, relX: usize, relY: usize, relZ: usize) ?[]const u8 {
self.mutex.lock();
defer self.mutex.unlock();
const index = getIndex(relX, relY, relZ);
@ -259,7 +259,7 @@ pub const ChunkCompression = struct { // MARK: ChunkCompression
deflate_with_8bit_palette = 3,
_,
};
pub fn compressChunk(allocator: main.utils.NeverFailingAllocator, ch: *chunk.Chunk, allowLossy: bool) []const u8 {
pub fn compressChunk(allocator: main.heap.NeverFailingAllocator, ch: *chunk.Chunk, allowLossy: bool) []const u8 {
if(ch.data.paletteLength == 1) {
var writer = BinaryWriter.initCapacity(allocator, .big, @sizeOf(CompressionAlgo) + @sizeOf(u32));

View File

@ -8,7 +8,7 @@ const ChunkPosition = main.chunk.ChunkPosition;
const ZonElement = main.ZonElement;
const vec = main.vec;
const Vec3i = vec.Vec3i;
const NeverFailingAllocator = main.utils.NeverFailingAllocator;
const NeverFailingAllocator = main.heap.NeverFailingAllocator;
const terrain = @import("terrain.zig");
const TerrainGenerationProfile = terrain.TerrainGenerationProfile;
@ -147,7 +147,7 @@ pub const InterpolatableCaveBiomeMapView = struct { // MARK: InterpolatableCaveB
width: i32,
allocator: NeverFailingAllocator,
pub fn init(allocator: main.utils.NeverFailingAllocator, pos: ChunkPosition, width: u31, margin: u31) InterpolatableCaveBiomeMapView {
pub fn init(allocator: main.heap.NeverFailingAllocator, pos: ChunkPosition, width: u31, margin: u31) InterpolatableCaveBiomeMapView {
const center = Vec3i{
pos.wx +% width/2,
pos.wy +% width/2,

View File

@ -6,7 +6,7 @@ const ServerChunk = main.chunk.ServerChunk;
const ChunkPosition = main.chunk.ChunkPosition;
const Cache = main.utils.Cache;
const ZonElement = main.ZonElement;
const NeverFailingAllocator = main.utils.NeverFailingAllocator;
const NeverFailingAllocator = main.heap.NeverFailingAllocator;
const terrain = @import("terrain.zig");
const TerrainGenerationProfile = terrain.TerrainGenerationProfile;

View File

@ -9,7 +9,7 @@ const terrain = main.server.terrain;
const TerrainGenerationProfile = terrain.TerrainGenerationProfile;
const Biome = terrain.biomes.Biome;
const MapFragment = terrain.SurfaceMap.MapFragment;
const NeverFailingAllocator = main.utils.NeverFailingAllocator;
const NeverFailingAllocator = main.heap.NeverFailingAllocator;
pub const BiomeSample = struct {
biome: *const Biome,

View File

@ -6,7 +6,7 @@ const ServerChunk = main.chunk.ServerChunk;
const ChunkPosition = main.chunk.ChunkPosition;
const Cache = main.utils.Cache;
const ZonElement = main.ZonElement;
const NeverFailingAllocator = main.utils.NeverFailingAllocator;
const NeverFailingAllocator = main.heap.NeverFailingAllocator;
const vec = main.vec;
const Vec3i = vec.Vec3i;
@ -41,8 +41,8 @@ pub const StructureMapFragment = struct {
pos: ChunkPosition,
voxelShift: u5,
refCount: Atomic(u16) = .init(0),
arena: main.utils.NeverFailingArenaAllocator,
allocator: main.utils.NeverFailingAllocator,
arena: main.heap.NeverFailingArenaAllocator,
allocator: main.heap.NeverFailingAllocator,
tempData: struct {
lists: *[chunkedSize*chunkedSize*chunkedSize]main.ListUnmanaged(Structure),

View File

@ -5,7 +5,7 @@ const blocks = main.blocks;
const ServerChunk = main.chunk.ServerChunk;
const ZonElement = main.ZonElement;
const terrain = main.server.terrain;
const NeverFailingAllocator = main.utils.NeverFailingAllocator;
const NeverFailingAllocator = main.heap.NeverFailingAllocator;
const vec = @import("main.vec");
const Vec3f = main.vec.Vec3f;
const Vec3d = main.vec.Vec3d;
@ -51,7 +51,7 @@ pub const SimpleStructureModel = struct { // MARK: SimpleStructureModel
}
var modelRegistry: std.StringHashMapUnmanaged(VTable) = .{};
var arena: main.utils.NeverFailingArenaAllocator = .init(main.globalAllocator);
var arena: main.heap.NeverFailingArenaAllocator = .init(main.globalAllocator);
pub fn reset() void {
std.debug.assert(arena.reset(.free_all));

View File

@ -13,7 +13,7 @@ const vec = main.vec;
const Vec2i = vec.Vec2i;
const Vec2f = vec.Vec2f;
const NeverFailingAllocator = main.utils.NeverFailingAllocator;
const NeverFailingAllocator = main.heap.NeverFailingAllocator;
// Generates the climate map using a fluidynamics simulation, with a circular heat distribution.

View File

@ -3,7 +3,7 @@ const std = @import("std");
const main = @import("root");
const random = main.random;
const Array2D = main.utils.Array2D;
const NeverFailingAllocator = main.utils.NeverFailingAllocator;
const NeverFailingAllocator = main.heap.NeverFailingAllocator;
const sizeShift = 7; // TODO: Increase back to 10 once this is no longer impacting loading time.
const size = 1 << sizeShift;

View File

@ -4,7 +4,7 @@ const main = @import("root");
const Array3D = main.utils.Array3D;
const ChunkPosition = main.chunk.ChunkPosition;
const random = main.random;
const NeverFailingAllocator = main.utils.NeverFailingAllocator;
const NeverFailingAllocator = main.heap.NeverFailingAllocator;
const FractalNoise3D = @This();

View File

@ -3,7 +3,7 @@ const std = @import("std");
const main = @import("root");
const Array2D = main.utils.Array2D;
const random = main.random;
const NeverFailingAllocator = main.utils.NeverFailingAllocator;
const NeverFailingAllocator = main.heap.NeverFailingAllocator;
// TODO: Simplify with Vec2f and Vec2i.

View File

@ -9,7 +9,7 @@ const vec = main.vec;
const Vec3d = vec.Vec3d;
const Vec3f = vec.Vec3f;
const Vec3i = vec.Vec3i;
const NeverFailingAllocator = main.utils.NeverFailingAllocator;
const NeverFailingAllocator = main.heap.NeverFailingAllocator;
pub const id = "cubyz:boulder";

View File

@ -9,7 +9,7 @@ const vec = main.vec;
const Vec3d = vec.Vec3d;
const Vec3f = vec.Vec3f;
const Vec3i = vec.Vec3i;
const NeverFailingAllocator = main.utils.NeverFailingAllocator;
const NeverFailingAllocator = main.heap.NeverFailingAllocator;
pub const id = "cubyz:fallen_tree";

View File

@ -9,7 +9,7 @@ const vec = main.vec;
const Vec3d = vec.Vec3d;
const Vec3f = vec.Vec3f;
const Vec3i = vec.Vec3i;
const NeverFailingAllocator = main.utils.NeverFailingAllocator;
const NeverFailingAllocator = main.heap.NeverFailingAllocator;
pub const id = "cubyz:flower_patch";

View File

@ -9,7 +9,7 @@ const vec = main.vec;
const Vec3d = vec.Vec3d;
const Vec3f = vec.Vec3f;
const Vec3i = vec.Vec3i;
const NeverFailingAllocator = main.utils.NeverFailingAllocator;
const NeverFailingAllocator = main.heap.NeverFailingAllocator;
pub const id = "cubyz:ground_patch";

View File

@ -9,7 +9,7 @@ const vec = main.vec;
const Vec3d = vec.Vec3d;
const Vec3f = vec.Vec3f;
const Vec3i = vec.Vec3i;
const NeverFailingAllocator = main.utils.NeverFailingAllocator;
const NeverFailingAllocator = main.heap.NeverFailingAllocator;
pub const id = "cubyz:simple_tree";

View File

@ -9,7 +9,7 @@ const vec = main.vec;
const Vec3d = vec.Vec3d;
const Vec3f = vec.Vec3f;
const Vec3i = vec.Vec3i;
const NeverFailingAllocator = main.utils.NeverFailingAllocator;
const NeverFailingAllocator = main.heap.NeverFailingAllocator;
pub const id = "cubyz:simple_vegetation";

View File

@ -9,7 +9,7 @@ const vec = main.vec;
const Vec3d = vec.Vec3d;
const Vec3f = vec.Vec3f;
const Vec3i = vec.Vec3i;
const NeverFailingAllocator = main.utils.NeverFailingAllocator;
const NeverFailingAllocator = main.heap.NeverFailingAllocator;
pub const id = "cubyz:stalagmite";

View File

@ -2,7 +2,7 @@ const std = @import("std");
const main = @import("root");
const ZonElement = main.ZonElement;
const NeverFailingAllocator = main.utils.NeverFailingAllocator;
const NeverFailingAllocator = main.heap.NeverFailingAllocator;
pub const biomes = @import("biomes.zig");
pub const noise = @import("noise/noise.zig");

View File

@ -497,7 +497,7 @@ pub const ServerWorld = struct { // MARK: ServerWorld
self.itemDropManager.init(main.globalAllocator, self, self.gravity);
errdefer self.itemDropManager.deinit();
var loadArena = main.utils.NeverFailingArenaAllocator.init(main.stackAllocator);
var loadArena = main.heap.NeverFailingArenaAllocator.init(main.stackAllocator);
defer loadArena.deinit();
const arenaAllocator = loadArena.allocator();
var buf: [32768]u8 = undefined;

View File

@ -4,6 +4,7 @@ const Atomic = std.atomic.Value;
const builtin = @import("builtin");
const main = @import("main.zig");
const NeverFailingAllocator = main.heap.NeverFailingAllocator;
pub const file_monitor = @import("utils/file_monitor.zig");
@ -424,542 +425,6 @@ pub fn ConcurrentQueue(comptime T: type) type { // MARK: ConcurrentQueue
};
}
/// Allows for stack-like allocations in a fast and safe way.
/// It is safe in the sense that a regular allocator will be used when the buffer is full.
pub const StackAllocator = struct { // MARK: StackAllocator
const AllocationTrailer = packed struct {wasFreed: bool, previousAllocationTrailer: u31};
backingAllocator: NeverFailingAllocator,
buffer: []align(4096) u8,
index: usize,
pub fn init(backingAllocator: NeverFailingAllocator, size: u31) StackAllocator {
return .{
.backingAllocator = backingAllocator,
.buffer = backingAllocator.alignedAlloc(u8, 4096, size),
.index = 0,
};
}
pub fn deinit(self: StackAllocator) void {
if(self.index != 0) {
std.log.err("Memory leak in Stack Allocator", .{});
}
self.backingAllocator.free(self.buffer);
}
pub fn allocator(self: *StackAllocator) NeverFailingAllocator {
return .{
.allocator = .{
.vtable = &.{
.alloc = &alloc,
.resize = &resize,
.remap = &remap,
.free = &free,
},
.ptr = self,
},
.IAssertThatTheProvidedAllocatorCantFail = {},
};
}
fn isInsideBuffer(self: *StackAllocator, buf: []u8) bool {
const bufferStart = @intFromPtr(self.buffer.ptr);
const bufferEnd = bufferStart + self.buffer.len;
const compare = @intFromPtr(buf.ptr);
return compare >= bufferStart and compare < bufferEnd;
}
fn indexInBuffer(self: *StackAllocator, buf: []u8) usize {
const bufferStart = @intFromPtr(self.buffer.ptr);
const compare = @intFromPtr(buf.ptr);
return compare - bufferStart;
}
fn getTrueAllocationEnd(start: usize, len: usize) usize {
const trailerStart = std.mem.alignForward(usize, start + len, @alignOf(AllocationTrailer));
return trailerStart + @sizeOf(AllocationTrailer);
}
fn getTrailerBefore(self: *StackAllocator, end: usize) *AllocationTrailer {
const trailerStart = end - @sizeOf(AllocationTrailer);
return @ptrCast(@alignCast(self.buffer[trailerStart..].ptr));
}
fn alloc(ctx: *anyopaque, len: usize, alignment: std.mem.Alignment, ret_addr: usize) ?[*]u8 {
const self: *StackAllocator = @ptrCast(@alignCast(ctx));
const start = std.mem.alignForward(usize, self.index, @as(usize, 1) << @intCast(@intFromEnum(alignment)));
const end = getTrueAllocationEnd(start, len);
if(end >= self.buffer.len) return self.backingAllocator.rawAlloc(len, alignment, ret_addr);
const trailer = self.getTrailerBefore(end);
trailer.* = .{.wasFreed = false, .previousAllocationTrailer = @intCast(self.index)};
self.index = end;
return self.buffer.ptr + start;
}
fn resize(ctx: *anyopaque, memory: []u8, alignment: std.mem.Alignment, new_len: usize, ret_addr: usize) bool {
const self: *StackAllocator = @ptrCast(@alignCast(ctx));
if(self.isInsideBuffer(memory)) {
const start = self.indexInBuffer(memory);
const end = getTrueAllocationEnd(start, memory.len);
if(end != self.index) return false;
const newEnd = getTrueAllocationEnd(start, new_len);
if(newEnd >= self.buffer.len) return false;
const trailer = self.getTrailerBefore(end);
std.debug.assert(!trailer.wasFreed);
const newTrailer = self.getTrailerBefore(newEnd);
newTrailer.* = .{.wasFreed = false, .previousAllocationTrailer = trailer.previousAllocationTrailer};
self.index = newEnd;
return true;
} else {
return self.backingAllocator.rawResize(memory, alignment, new_len, ret_addr);
}
}
fn remap(ctx: *anyopaque, memory: []u8, alignment: std.mem.Alignment, new_len: usize, ret_addr: usize) ?[*]u8 {
if(resize(ctx, memory, alignment, new_len, ret_addr)) return memory.ptr;
return null;
}
fn free(ctx: *anyopaque, memory: []u8, alignment: std.mem.Alignment, ret_addr: usize) void {
const self: *StackAllocator = @ptrCast(@alignCast(ctx));
if(self.isInsideBuffer(memory)) {
const start = self.indexInBuffer(memory);
const end = getTrueAllocationEnd(start, memory.len);
const trailer = self.getTrailerBefore(end);
std.debug.assert(!trailer.wasFreed); // Double Free
if(end == self.index) {
self.index = trailer.previousAllocationTrailer;
if(self.index != 0) {
var previousTrailer = self.getTrailerBefore(trailer.previousAllocationTrailer);
while(previousTrailer.wasFreed) {
self.index = previousTrailer.previousAllocationTrailer;
if(self.index == 0) break;
previousTrailer = self.getTrailerBefore(previousTrailer.previousAllocationTrailer);
}
}
} else {
trailer.wasFreed = true;
}
} else {
self.backingAllocator.rawFree(memory, alignment, ret_addr);
}
}
};
/// An allocator that handles OutOfMemory situations by panicing or freeing memory(TODO), making it safe to ignore errors.
pub const ErrorHandlingAllocator = struct { // MARK: ErrorHandlingAllocator
backingAllocator: Allocator,
pub fn init(backingAllocator: Allocator) ErrorHandlingAllocator {
return .{
.backingAllocator = backingAllocator,
};
}
pub fn allocator(self: *ErrorHandlingAllocator) NeverFailingAllocator {
return .{
.allocator = .{
.vtable = &.{
.alloc = &alloc,
.resize = &resize,
.remap = &remap,
.free = &free,
},
.ptr = self,
},
.IAssertThatTheProvidedAllocatorCantFail = {},
};
}
fn handleError() noreturn {
@panic("Out Of Memory. Please download more RAM, reduce the render distance, or close some of your 100 browser tabs.");
}
/// Return a pointer to `len` bytes with specified `alignment`, or return
/// `null` indicating the allocation failed.
///
/// `ret_addr` is optionally provided as the first return address of the
/// allocation call stack. If the value is `0` it means no return address
/// has been provided.
fn alloc(ctx: *anyopaque, len: usize, alignment: std.mem.Alignment, ret_addr: usize) ?[*]u8 {
const self: *ErrorHandlingAllocator = @ptrCast(@alignCast(ctx));
return self.backingAllocator.rawAlloc(len, alignment, ret_addr) orelse handleError();
}
/// Attempt to expand or shrink memory in place.
///
/// `memory.len` must equal the length requested from the most recent
/// successful call to `alloc`, `resize`, or `remap`. `alignment` must
/// equal the same value that was passed as the `alignment` parameter to
/// the original `alloc` call.
///
/// A result of `true` indicates the resize was successful and the
/// allocation now has the same address but a size of `new_len`. `false`
/// indicates the resize could not be completed without moving the
/// allocation to a different address.
///
/// `new_len` must be greater than zero.
///
/// `ret_addr` is optionally provided as the first return address of the
/// allocation call stack. If the value is `0` it means no return address
/// has been provided.
fn resize(ctx: *anyopaque, memory: []u8, alignment: std.mem.Alignment, new_len: usize, ret_addr: usize) bool {
const self: *ErrorHandlingAllocator = @ptrCast(@alignCast(ctx));
return self.backingAllocator.rawResize(memory, alignment, new_len, ret_addr);
}
/// Attempt to expand or shrink memory, allowing relocation.
///
/// `memory.len` must equal the length requested from the most recent
/// successful call to `alloc`, `resize`, or `remap`. `alignment` must
/// equal the same value that was passed as the `alignment` parameter to
/// the original `alloc` call.
///
/// A non-`null` return value indicates the resize was successful. The
/// allocation may have same address, or may have been relocated. In either
/// case, the allocation now has size of `new_len`. A `null` return value
/// indicates that the resize would be equivalent to allocating new memory,
/// copying the bytes from the old memory, and then freeing the old memory.
/// In such case, it is more efficient for the caller to perform the copy.
///
/// `new_len` must be greater than zero.
///
/// `ret_addr` is optionally provided as the first return address of the
/// allocation call stack. If the value is `0` it means no return address
/// has been provided.
fn remap(ctx: *anyopaque, memory: []u8, alignment: std.mem.Alignment, new_len: usize, ret_addr: usize) ?[*]u8 {
const self: *ErrorHandlingAllocator = @ptrCast(@alignCast(ctx));
return self.backingAllocator.rawRemap(memory, alignment, new_len, ret_addr);
}
/// Free and invalidate a region of memory.
///
/// `memory.len` must equal the length requested from the most recent
/// successful call to `alloc`, `resize`, or `remap`. `alignment` must
/// equal the same value that was passed as the `alignment` parameter to
/// the original `alloc` call.
///
/// `ret_addr` is optionally provided as the first return address of the
/// allocation call stack. If the value is `0` it means no return address
/// has been provided.
fn free(ctx: *anyopaque, memory: []u8, alignment: std.mem.Alignment, ret_addr: usize) void {
const self: *ErrorHandlingAllocator = @ptrCast(@alignCast(ctx));
self.backingAllocator.rawFree(memory, alignment, ret_addr);
}
};
/// An allocator interface signaling that you can use
pub const NeverFailingAllocator = struct { // MARK: NeverFailingAllocator
allocator: Allocator,
IAssertThatTheProvidedAllocatorCantFail: void,
const Alignment = std.mem.Alignment;
const math = std.math;
/// This function is not intended to be called except from within the
/// implementation of an `Allocator`.
pub inline fn rawAlloc(a: NeverFailingAllocator, len: usize, alignment: Alignment, ret_addr: usize) ?[*]u8 {
return a.allocator.vtable.alloc(a.allocator.ptr, len, alignment, ret_addr);
}
/// This function is not intended to be called except from within the
/// implementation of an `Allocator`.
pub inline fn rawResize(a: NeverFailingAllocator, memory: []u8, alignment: Alignment, new_len: usize, ret_addr: usize) bool {
return a.allocator.vtable.resize(a.allocator.ptr, memory, alignment, new_len, ret_addr);
}
/// This function is not intended to be called except from within the
/// implementation of an `Allocator`.
pub inline fn rawRemap(a: NeverFailingAllocator, memory: []u8, alignment: Alignment, new_len: usize, ret_addr: usize) ?[*]u8 {
return a.allocator.vtable.remap(a.allocator.ptr, memory, alignment, new_len, ret_addr);
}
/// This function is not intended to be called except from within the
/// implementation of an `Allocator`.
pub inline fn rawFree(a: NeverFailingAllocator, memory: []u8, alignment: Alignment, ret_addr: usize) void {
return a.allocator.vtable.free(a.allocator.ptr, memory, alignment, ret_addr);
}
/// Returns a pointer to undefined memory.
/// Call `destroy` with the result to free the memory.
pub fn create(self: NeverFailingAllocator, comptime T: type) *T {
return self.allocator.create(T) catch unreachable;
}
/// `ptr` should be the return value of `create`, or otherwise
/// have the same address and alignment property.
pub fn destroy(self: NeverFailingAllocator, ptr: anytype) void {
self.allocator.destroy(ptr);
}
/// Allocates an array of `n` items of type `T` and sets all the
/// items to `undefined`. Depending on the Allocator
/// implementation, it may be required to call `free` once the
/// memory is no longer needed, to avoid a resource leak. If the
/// `Allocator` implementation is unknown, then correct code will
/// call `free` when done.
///
/// For allocating a single item, see `create`.
pub fn alloc(self: NeverFailingAllocator, comptime T: type, n: usize) []T {
return self.allocator.alloc(T, n) catch unreachable;
}
pub fn allocWithOptions(
self: NeverFailingAllocator,
comptime Elem: type,
n: usize,
/// null means naturally aligned
comptime optional_alignment: ?u29,
comptime optional_sentinel: ?Elem,
) AllocWithOptionsPayload(Elem, optional_alignment, optional_sentinel) {
return self.allocator.allocWithOptions(Elem, n, optional_alignment, optional_sentinel) catch unreachable;
}
pub fn allocWithOptionsRetAddr(
self: NeverFailingAllocator,
comptime Elem: type,
n: usize,
/// null means naturally aligned
comptime optional_alignment: ?u29,
comptime optional_sentinel: ?Elem,
return_address: usize,
) AllocWithOptionsPayload(Elem, optional_alignment, optional_sentinel) {
return self.allocator.allocWithOptionsRetAddr(Elem, n, optional_alignment, optional_sentinel, return_address) catch unreachable;
}
fn AllocWithOptionsPayload(comptime Elem: type, comptime alignment: ?u29, comptime sentinel: ?Elem) type {
if(sentinel) |s| {
return [:s]align(alignment orelse @alignOf(Elem)) Elem;
} else {
return []align(alignment orelse @alignOf(Elem)) Elem;
}
}
/// Allocates an array of `n + 1` items of type `T` and sets the first `n`
/// items to `undefined` and the last item to `sentinel`. Depending on the
/// Allocator implementation, it may be required to call `free` once the
/// memory is no longer needed, to avoid a resource leak. If the
/// `Allocator` implementation is unknown, then correct code will
/// call `free` when done.
///
/// For allocating a single item, see `create`.
pub fn allocSentinel(
self: NeverFailingAllocator,
comptime Elem: type,
n: usize,
comptime sentinel: Elem,
) [:sentinel]Elem {
return self.allocator.allocSentinel(Elem, n, sentinel) catch unreachable;
}
pub fn alignedAlloc(
self: NeverFailingAllocator,
comptime T: type,
/// null means naturally aligned
comptime alignment: ?u29,
n: usize,
) []align(alignment orelse @alignOf(T)) T {
return self.allocator.alignedAlloc(T, alignment, n) catch unreachable;
}
pub inline fn allocAdvancedWithRetAddr(
self: NeverFailingAllocator,
comptime T: type,
/// null means naturally aligned
comptime alignment: ?u29,
n: usize,
return_address: usize,
) []align(alignment orelse @alignOf(T)) T {
return self.allocator.allocAdvancedWithRetAddr(T, alignment, n, return_address) catch unreachable;
}
fn allocWithSizeAndAlignment(self: NeverFailingAllocator, comptime size: usize, comptime alignment: u29, n: usize, return_address: usize) [*]align(alignment) u8 {
return self.allocator.allocWithSizeAndAlignment(alignment, size, alignment, n, return_address) catch unreachable;
}
fn allocBytesWithAlignment(self: NeverFailingAllocator, comptime alignment: u29, byte_count: usize, return_address: usize) [*]align(alignment) u8 {
return self.allocator.allocBytesWithAlignment(alignment, byte_count, return_address) catch unreachable;
}
/// Request to modify the size of an allocation.
///
/// It is guaranteed to not move the pointer, however the allocator
/// implementation may refuse the resize request by returning `false`.
///
/// `allocation` may be an empty slice, in which case a new allocation is made.
///
/// `new_len` may be zero, in which case the allocation is freed.
pub fn resize(self: NeverFailingAllocator, allocation: anytype, new_len: usize) bool {
return self.allocator.resize(allocation, new_len);
}
/// Request to modify the size of an allocation, allowing relocation.
///
/// A non-`null` return value indicates the resize was successful. The
/// allocation may have same address, or may have been relocated. In either
/// case, the allocation now has size of `new_len`. A `null` return value
/// indicates that the resize would be equivalent to allocating new memory,
/// copying the bytes from the old memory, and then freeing the old memory.
/// In such case, it is more efficient for the caller to perform those
/// operations.
///
/// `allocation` may be an empty slice, in which case a new allocation is made.
///
/// `new_len` may be zero, in which case the allocation is freed.
pub fn remap(self: NeverFailingAllocator, allocation: anytype, new_len: usize) t: {
const Slice = @typeInfo(@TypeOf(allocation)).pointer;
break :t ?[]align(Slice.alignment) Slice.child;
} {
return self.allocator.remap(allocation, new_len);
}
/// This function requests a new byte size for an existing allocation, which
/// can be larger, smaller, or the same size as the old memory allocation.
///
/// If `new_n` is 0, this is the same as `free` and it always succeeds.
///
/// `old_mem` may have length zero, which makes a new allocation.
///
/// This function only fails on out-of-memory conditions, unlike:
/// * `remap` which returns `null` when the `Allocator` implementation cannot
/// do the realloc more efficiently than the caller
/// * `resize` which returns `false` when the `Allocator` implementation cannot
/// change the size without relocating the allocation.
pub fn realloc(self: NeverFailingAllocator, old_mem: anytype, new_n: usize) t: {
const Slice = @typeInfo(@TypeOf(old_mem)).pointer;
break :t []align(Slice.alignment) Slice.child;
} {
return self.allocator.realloc(old_mem, new_n) catch unreachable;
}
pub fn reallocAdvanced(
self: NeverFailingAllocator,
old_mem: anytype,
new_n: usize,
return_address: usize,
) t: {
const Slice = @typeInfo(@TypeOf(old_mem)).pointer;
break :t []align(Slice.alignment) Slice.child;
} {
return self.allocator.reallocAdvanced(old_mem, new_n, return_address) catch unreachable;
}
/// Free an array allocated with `alloc`.
/// If memory has length 0, free is a no-op.
/// To free a single item, see `destroy`.
pub fn free(self: NeverFailingAllocator, memory: anytype) void {
self.allocator.free(memory);
}
/// Copies `m` to newly allocated memory. Caller owns the memory.
pub fn dupe(self: NeverFailingAllocator, comptime T: type, m: []const T) []T {
return self.allocator.dupe(T, m) catch unreachable;
}
/// Copies `m` to newly allocated memory, with a null-terminated element. Caller owns the memory.
pub fn dupeZ(self: NeverFailingAllocator, comptime T: type, m: []const T) [:0]T {
return self.allocator.dupeZ(T, m) catch unreachable;
}
};
pub const NeverFailingArenaAllocator = struct { // MARK: NeverFailingArena
arena: std.heap.ArenaAllocator,
pub fn init(child_allocator: NeverFailingAllocator) NeverFailingArenaAllocator {
return .{
.arena = .init(child_allocator.allocator),
};
}
pub fn deinit(self: NeverFailingArenaAllocator) void {
self.arena.deinit();
}
pub fn allocator(self: *NeverFailingArenaAllocator) NeverFailingAllocator {
return .{
.allocator = self.arena.allocator(),
.IAssertThatTheProvidedAllocatorCantFail = {},
};
}
/// Resets the arena allocator and frees all allocated memory.
///
/// `mode` defines how the currently allocated memory is handled.
/// See the variant documentation for `ResetMode` for the effects of each mode.
///
/// The function will return whether the reset operation was successful or not.
/// If the reallocation failed `false` is returned. The arena will still be fully
/// functional in that case, all memory is released. Future allocations just might
/// be slower.
///
/// NOTE: If `mode` is `free_all`, the function will always return `true`.
pub fn reset(self: *NeverFailingArenaAllocator, mode: std.heap.ArenaAllocator.ResetMode) bool {
return self.arena.reset(mode);
}
pub fn shrinkAndFree(self: *NeverFailingArenaAllocator) void {
const node = self.arena.state.buffer_list.first orelse return;
const allocBuf = @as([*]u8, @ptrCast(node))[0..node.data];
const dataSize = std.mem.alignForward(usize, @sizeOf(std.SinglyLinkedList(usize).Node) + self.arena.state.end_index, @alignOf(std.SinglyLinkedList(usize).Node));
if(self.arena.child_allocator.rawResize(allocBuf, @enumFromInt(std.math.log2(@alignOf(std.SinglyLinkedList(usize).Node))), dataSize, @returnAddress())) {
node.data = dataSize;
}
}
};
pub const BufferFallbackAllocator = struct { // MARK: BufferFallbackAllocator
fixedBuffer: std.heap.FixedBufferAllocator,
fallbackAllocator: NeverFailingAllocator,
pub fn init(buffer: []u8, fallbackAllocator: NeverFailingAllocator) BufferFallbackAllocator {
return .{
.fixedBuffer = .init(buffer),
.fallbackAllocator = fallbackAllocator,
};
}
pub fn allocator(self: *BufferFallbackAllocator) NeverFailingAllocator {
return .{
.allocator = .{
.vtable = &.{
.alloc = &alloc,
.resize = &resize,
.free = &free,
},
.ptr = self,
},
.IAssertThatTheProvidedAllocatorCantFail = {},
};
}
fn alloc(ctx: *anyopaque, len: usize, log2_ptr_align: u8, ra: usize) ?[*]u8 {
const self: *BufferFallbackAllocator = @ptrCast(@alignCast(ctx));
return self.fixedBuffer.allocator().rawAlloc(len, log2_ptr_align, ra) orelse
return self.fallbackAllocator.rawAlloc(len, log2_ptr_align, ra);
}
fn resize(ctx: *anyopaque, buf: []u8, log2_buf_align: u8, new_len: usize, ra: usize) bool {
const self: *BufferFallbackAllocator = @ptrCast(@alignCast(ctx));
if(self.fixedBuffer.ownsPtr(buf.ptr)) {
return self.fixedBuffer.allocator().rawResize(buf, log2_buf_align, new_len, ra);
} else {
return self.fallbackAllocator.rawResize(buf, log2_buf_align, new_len, ra);
}
}
fn free(ctx: *anyopaque, buf: []u8, log2_buf_align: u8, ra: usize) void {
const self: *BufferFallbackAllocator = @ptrCast(@alignCast(ctx));
if(self.fixedBuffer.ownsPtr(buf.ptr)) {
return self.fixedBuffer.allocator().rawFree(buf, log2_buf_align, ra);
} else {
return self.fallbackAllocator.rawFree(buf, log2_buf_align, ra);
}
}
};
/// A simple binary heap.
/// Thread safe and blocking.
/// Expects T to have a `biggerThan(T) bool` function
@ -1242,7 +707,7 @@ pub const ThreadPool = struct { // MARK: ThreadPool
fn run(self: *ThreadPool, id: usize) void {
// In case any of the tasks wants to allocate memory:
var sta = StackAllocator.init(main.globalAllocator, 1 << 23);
var sta = main.heap.StackAllocator.init(main.globalAllocator, 1 << 23);
defer sta.deinit();
main.stackAllocator = sta.allocator();
@ -1321,7 +786,7 @@ pub fn DynamicPackedIntArray(size: comptime_int) type { // MARK: DynamicPackedIn
const Self = @This();
pub fn initCapacity(allocator: main.utils.NeverFailingAllocator, bitSize: u5) Self {
pub fn initCapacity(allocator: main.heap.NeverFailingAllocator, bitSize: u5) Self {
std.debug.assert(bitSize == 0 or bitSize & bitSize - 1 == 0); // Must be a power of 2
return .{
.data = allocator.alignedAlloc(u32, 64, @as(usize, @divExact(size, @bitSizeOf(u32)))*bitSize),
@ -1329,7 +794,7 @@ pub fn DynamicPackedIntArray(size: comptime_int) type { // MARK: DynamicPackedIn
};
}
pub fn deinit(self: *Self, allocator: main.utils.NeverFailingAllocator) void {
pub fn deinit(self: *Self, allocator: main.heap.NeverFailingAllocator) void {
allocator.free(self.data);
self.* = .{};
}
@ -1343,7 +808,7 @@ pub fn DynamicPackedIntArray(size: comptime_int) type { // MARK: DynamicPackedIn
return result;
}
pub fn resizeOnce(self: *Self, allocator: main.utils.NeverFailingAllocator) void {
pub fn resizeOnce(self: *Self, allocator: main.heap.NeverFailingAllocator) void {
const newBitSize = if(self.bitSize != 0) self.bitSize*2 else 1;
var newSelf = Self.initCapacity(allocator, newBitSize);

490
src/utils/heap.zig Normal file
View File

@ -0,0 +1,490 @@
const std = @import("std");
const Allocator = std.mem.Allocator;
const main = @import("root");
/// Allows for stack-like allocations in a fast and safe way.
/// It is safe in the sense that a regular allocator will be used when the buffer is full.
pub const StackAllocator = struct { // MARK: StackAllocator
const AllocationTrailer = packed struct {wasFreed: bool, previousAllocationTrailer: u31};
backingAllocator: NeverFailingAllocator,
buffer: []align(4096) u8,
index: usize,
pub fn init(backingAllocator: NeverFailingAllocator, size: u31) StackAllocator {
return .{
.backingAllocator = backingAllocator,
.buffer = backingAllocator.alignedAlloc(u8, 4096, size),
.index = 0,
};
}
pub fn deinit(self: StackAllocator) void {
if(self.index != 0) {
std.log.err("Memory leak in Stack Allocator", .{});
}
self.backingAllocator.free(self.buffer);
}
pub fn allocator(self: *StackAllocator) NeverFailingAllocator {
return .{
.allocator = .{
.vtable = &.{
.alloc = &alloc,
.resize = &resize,
.remap = &remap,
.free = &free,
},
.ptr = self,
},
.IAssertThatTheProvidedAllocatorCantFail = {},
};
}
fn isInsideBuffer(self: *StackAllocator, buf: []u8) bool {
const bufferStart = @intFromPtr(self.buffer.ptr);
const bufferEnd = bufferStart + self.buffer.len;
const compare = @intFromPtr(buf.ptr);
return compare >= bufferStart and compare < bufferEnd;
}
fn indexInBuffer(self: *StackAllocator, buf: []u8) usize {
const bufferStart = @intFromPtr(self.buffer.ptr);
const compare = @intFromPtr(buf.ptr);
return compare - bufferStart;
}
fn getTrueAllocationEnd(start: usize, len: usize) usize {
const trailerStart = std.mem.alignForward(usize, start + len, @alignOf(AllocationTrailer));
return trailerStart + @sizeOf(AllocationTrailer);
}
fn getTrailerBefore(self: *StackAllocator, end: usize) *AllocationTrailer {
const trailerStart = end - @sizeOf(AllocationTrailer);
return @ptrCast(@alignCast(self.buffer[trailerStart..].ptr));
}
fn alloc(ctx: *anyopaque, len: usize, alignment: std.mem.Alignment, ret_addr: usize) ?[*]u8 {
const self: *StackAllocator = @ptrCast(@alignCast(ctx));
const start = std.mem.alignForward(usize, self.index, @as(usize, 1) << @intCast(@intFromEnum(alignment)));
const end = getTrueAllocationEnd(start, len);
if(end >= self.buffer.len) return self.backingAllocator.rawAlloc(len, alignment, ret_addr);
const trailer = self.getTrailerBefore(end);
trailer.* = .{.wasFreed = false, .previousAllocationTrailer = @intCast(self.index)};
self.index = end;
return self.buffer.ptr + start;
}
fn resize(ctx: *anyopaque, memory: []u8, alignment: std.mem.Alignment, new_len: usize, ret_addr: usize) bool {
const self: *StackAllocator = @ptrCast(@alignCast(ctx));
if(self.isInsideBuffer(memory)) {
const start = self.indexInBuffer(memory);
const end = getTrueAllocationEnd(start, memory.len);
if(end != self.index) return false;
const newEnd = getTrueAllocationEnd(start, new_len);
if(newEnd >= self.buffer.len) return false;
const trailer = self.getTrailerBefore(end);
std.debug.assert(!trailer.wasFreed);
const newTrailer = self.getTrailerBefore(newEnd);
newTrailer.* = .{.wasFreed = false, .previousAllocationTrailer = trailer.previousAllocationTrailer};
self.index = newEnd;
return true;
} else {
return self.backingAllocator.rawResize(memory, alignment, new_len, ret_addr);
}
}
fn remap(ctx: *anyopaque, memory: []u8, alignment: std.mem.Alignment, new_len: usize, ret_addr: usize) ?[*]u8 {
if(resize(ctx, memory, alignment, new_len, ret_addr)) return memory.ptr;
return null;
}
fn free(ctx: *anyopaque, memory: []u8, alignment: std.mem.Alignment, ret_addr: usize) void {
const self: *StackAllocator = @ptrCast(@alignCast(ctx));
if(self.isInsideBuffer(memory)) {
const start = self.indexInBuffer(memory);
const end = getTrueAllocationEnd(start, memory.len);
const trailer = self.getTrailerBefore(end);
std.debug.assert(!trailer.wasFreed); // Double Free
if(end == self.index) {
self.index = trailer.previousAllocationTrailer;
if(self.index != 0) {
var previousTrailer = self.getTrailerBefore(trailer.previousAllocationTrailer);
while(previousTrailer.wasFreed) {
self.index = previousTrailer.previousAllocationTrailer;
if(self.index == 0) break;
previousTrailer = self.getTrailerBefore(previousTrailer.previousAllocationTrailer);
}
}
} else {
trailer.wasFreed = true;
}
} else {
self.backingAllocator.rawFree(memory, alignment, ret_addr);
}
}
};
/// An allocator that handles OutOfMemory situations by panicing or freeing memory(TODO), making it safe to ignore errors.
pub const ErrorHandlingAllocator = struct { // MARK: ErrorHandlingAllocator
backingAllocator: Allocator,
pub fn init(backingAllocator: Allocator) ErrorHandlingAllocator {
return .{
.backingAllocator = backingAllocator,
};
}
pub fn allocator(self: *ErrorHandlingAllocator) NeverFailingAllocator {
return .{
.allocator = .{
.vtable = &.{
.alloc = &alloc,
.resize = &resize,
.remap = &remap,
.free = &free,
},
.ptr = self,
},
.IAssertThatTheProvidedAllocatorCantFail = {},
};
}
fn handleError() noreturn {
@panic("Out Of Memory. Please download more RAM, reduce the render distance, or close some of your 100 browser tabs.");
}
/// Return a pointer to `len` bytes with specified `alignment`, or return
/// `null` indicating the allocation failed.
///
/// `ret_addr` is optionally provided as the first return address of the
/// allocation call stack. If the value is `0` it means no return address
/// has been provided.
fn alloc(ctx: *anyopaque, len: usize, alignment: std.mem.Alignment, ret_addr: usize) ?[*]u8 {
const self: *ErrorHandlingAllocator = @ptrCast(@alignCast(ctx));
return self.backingAllocator.rawAlloc(len, alignment, ret_addr) orelse handleError();
}
/// Attempt to expand or shrink memory in place.
///
/// `memory.len` must equal the length requested from the most recent
/// successful call to `alloc`, `resize`, or `remap`. `alignment` must
/// equal the same value that was passed as the `alignment` parameter to
/// the original `alloc` call.
///
/// A result of `true` indicates the resize was successful and the
/// allocation now has the same address but a size of `new_len`. `false`
/// indicates the resize could not be completed without moving the
/// allocation to a different address.
///
/// `new_len` must be greater than zero.
///
/// `ret_addr` is optionally provided as the first return address of the
/// allocation call stack. If the value is `0` it means no return address
/// has been provided.
fn resize(ctx: *anyopaque, memory: []u8, alignment: std.mem.Alignment, new_len: usize, ret_addr: usize) bool {
const self: *ErrorHandlingAllocator = @ptrCast(@alignCast(ctx));
return self.backingAllocator.rawResize(memory, alignment, new_len, ret_addr);
}
/// Attempt to expand or shrink memory, allowing relocation.
///
/// `memory.len` must equal the length requested from the most recent
/// successful call to `alloc`, `resize`, or `remap`. `alignment` must
/// equal the same value that was passed as the `alignment` parameter to
/// the original `alloc` call.
///
/// A non-`null` return value indicates the resize was successful. The
/// allocation may have same address, or may have been relocated. In either
/// case, the allocation now has size of `new_len`. A `null` return value
/// indicates that the resize would be equivalent to allocating new memory,
/// copying the bytes from the old memory, and then freeing the old memory.
/// In such case, it is more efficient for the caller to perform the copy.
///
/// `new_len` must be greater than zero.
///
/// `ret_addr` is optionally provided as the first return address of the
/// allocation call stack. If the value is `0` it means no return address
/// has been provided.
fn remap(ctx: *anyopaque, memory: []u8, alignment: std.mem.Alignment, new_len: usize, ret_addr: usize) ?[*]u8 {
const self: *ErrorHandlingAllocator = @ptrCast(@alignCast(ctx));
return self.backingAllocator.rawRemap(memory, alignment, new_len, ret_addr);
}
/// Free and invalidate a region of memory.
///
/// `memory.len` must equal the length requested from the most recent
/// successful call to `alloc`, `resize`, or `remap`. `alignment` must
/// equal the same value that was passed as the `alignment` parameter to
/// the original `alloc` call.
///
/// `ret_addr` is optionally provided as the first return address of the
/// allocation call stack. If the value is `0` it means no return address
/// has been provided.
fn free(ctx: *anyopaque, memory: []u8, alignment: std.mem.Alignment, ret_addr: usize) void {
const self: *ErrorHandlingAllocator = @ptrCast(@alignCast(ctx));
self.backingAllocator.rawFree(memory, alignment, ret_addr);
}
};
/// An allocator interface signaling that you can use
pub const NeverFailingAllocator = struct { // MARK: NeverFailingAllocator
allocator: Allocator,
IAssertThatTheProvidedAllocatorCantFail: void,
const Alignment = std.mem.Alignment;
const math = std.math;
/// This function is not intended to be called except from within the
/// implementation of an `Allocator`.
pub inline fn rawAlloc(a: NeverFailingAllocator, len: usize, alignment: Alignment, ret_addr: usize) ?[*]u8 {
return a.allocator.vtable.alloc(a.allocator.ptr, len, alignment, ret_addr);
}
/// This function is not intended to be called except from within the
/// implementation of an `Allocator`.
pub inline fn rawResize(a: NeverFailingAllocator, memory: []u8, alignment: Alignment, new_len: usize, ret_addr: usize) bool {
return a.allocator.vtable.resize(a.allocator.ptr, memory, alignment, new_len, ret_addr);
}
/// This function is not intended to be called except from within the
/// implementation of an `Allocator`.
pub inline fn rawRemap(a: NeverFailingAllocator, memory: []u8, alignment: Alignment, new_len: usize, ret_addr: usize) ?[*]u8 {
return a.allocator.vtable.remap(a.allocator.ptr, memory, alignment, new_len, ret_addr);
}
/// This function is not intended to be called except from within the
/// implementation of an `Allocator`.
pub inline fn rawFree(a: NeverFailingAllocator, memory: []u8, alignment: Alignment, ret_addr: usize) void {
return a.allocator.vtable.free(a.allocator.ptr, memory, alignment, ret_addr);
}
/// Returns a pointer to undefined memory.
/// Call `destroy` with the result to free the memory.
pub fn create(self: NeverFailingAllocator, comptime T: type) *T {
return self.allocator.create(T) catch unreachable;
}
/// `ptr` should be the return value of `create`, or otherwise
/// have the same address and alignment property.
pub fn destroy(self: NeverFailingAllocator, ptr: anytype) void {
self.allocator.destroy(ptr);
}
/// Allocates an array of `n` items of type `T` and sets all the
/// items to `undefined`. Depending on the Allocator
/// implementation, it may be required to call `free` once the
/// memory is no longer needed, to avoid a resource leak. If the
/// `Allocator` implementation is unknown, then correct code will
/// call `free` when done.
///
/// For allocating a single item, see `create`.
pub fn alloc(self: NeverFailingAllocator, comptime T: type, n: usize) []T {
return self.allocator.alloc(T, n) catch unreachable;
}
pub fn allocWithOptions(
self: NeverFailingAllocator,
comptime Elem: type,
n: usize,
/// null means naturally aligned
comptime optional_alignment: ?u29,
comptime optional_sentinel: ?Elem,
) AllocWithOptionsPayload(Elem, optional_alignment, optional_sentinel) {
return self.allocator.allocWithOptions(Elem, n, optional_alignment, optional_sentinel) catch unreachable;
}
pub fn allocWithOptionsRetAddr(
self: NeverFailingAllocator,
comptime Elem: type,
n: usize,
/// null means naturally aligned
comptime optional_alignment: ?u29,
comptime optional_sentinel: ?Elem,
return_address: usize,
) AllocWithOptionsPayload(Elem, optional_alignment, optional_sentinel) {
return self.allocator.allocWithOptionsRetAddr(Elem, n, optional_alignment, optional_sentinel, return_address) catch unreachable;
}
fn AllocWithOptionsPayload(comptime Elem: type, comptime alignment: ?u29, comptime sentinel: ?Elem) type {
if(sentinel) |s| {
return [:s]align(alignment orelse @alignOf(Elem)) Elem;
} else {
return []align(alignment orelse @alignOf(Elem)) Elem;
}
}
/// Allocates an array of `n + 1` items of type `T` and sets the first `n`
/// items to `undefined` and the last item to `sentinel`. Depending on the
/// Allocator implementation, it may be required to call `free` once the
/// memory is no longer needed, to avoid a resource leak. If the
/// `Allocator` implementation is unknown, then correct code will
/// call `free` when done.
///
/// For allocating a single item, see `create`.
pub fn allocSentinel(
self: NeverFailingAllocator,
comptime Elem: type,
n: usize,
comptime sentinel: Elem,
) [:sentinel]Elem {
return self.allocator.allocSentinel(Elem, n, sentinel) catch unreachable;
}
pub fn alignedAlloc(
self: NeverFailingAllocator,
comptime T: type,
/// null means naturally aligned
comptime alignment: ?u29,
n: usize,
) []align(alignment orelse @alignOf(T)) T {
return self.allocator.alignedAlloc(T, alignment, n) catch unreachable;
}
pub inline fn allocAdvancedWithRetAddr(
self: NeverFailingAllocator,
comptime T: type,
/// null means naturally aligned
comptime alignment: ?u29,
n: usize,
return_address: usize,
) []align(alignment orelse @alignOf(T)) T {
return self.allocator.allocAdvancedWithRetAddr(T, alignment, n, return_address) catch unreachable;
}
fn allocWithSizeAndAlignment(self: NeverFailingAllocator, comptime size: usize, comptime alignment: u29, n: usize, return_address: usize) [*]align(alignment) u8 {
return self.allocator.allocWithSizeAndAlignment(alignment, size, alignment, n, return_address) catch unreachable;
}
fn allocBytesWithAlignment(self: NeverFailingAllocator, comptime alignment: u29, byte_count: usize, return_address: usize) [*]align(alignment) u8 {
return self.allocator.allocBytesWithAlignment(alignment, byte_count, return_address) catch unreachable;
}
/// Request to modify the size of an allocation.
///
/// It is guaranteed to not move the pointer, however the allocator
/// implementation may refuse the resize request by returning `false`.
///
/// `allocation` may be an empty slice, in which case a new allocation is made.
///
/// `new_len` may be zero, in which case the allocation is freed.
pub fn resize(self: NeverFailingAllocator, allocation: anytype, new_len: usize) bool {
return self.allocator.resize(allocation, new_len);
}
/// Request to modify the size of an allocation, allowing relocation.
///
/// A non-`null` return value indicates the resize was successful. The
/// allocation may have same address, or may have been relocated. In either
/// case, the allocation now has size of `new_len`. A `null` return value
/// indicates that the resize would be equivalent to allocating new memory,
/// copying the bytes from the old memory, and then freeing the old memory.
/// In such case, it is more efficient for the caller to perform those
/// operations.
///
/// `allocation` may be an empty slice, in which case a new allocation is made.
///
/// `new_len` may be zero, in which case the allocation is freed.
pub fn remap(self: NeverFailingAllocator, allocation: anytype, new_len: usize) t: {
const Slice = @typeInfo(@TypeOf(allocation)).pointer;
break :t ?[]align(Slice.alignment) Slice.child;
} {
return self.allocator.remap(allocation, new_len);
}
/// This function requests a new byte size for an existing allocation, which
/// can be larger, smaller, or the same size as the old memory allocation.
///
/// If `new_n` is 0, this is the same as `free` and it always succeeds.
///
/// `old_mem` may have length zero, which makes a new allocation.
///
/// This function only fails on out-of-memory conditions, unlike:
/// * `remap` which returns `null` when the `Allocator` implementation cannot
/// do the realloc more efficiently than the caller
/// * `resize` which returns `false` when the `Allocator` implementation cannot
/// change the size without relocating the allocation.
pub fn realloc(self: NeverFailingAllocator, old_mem: anytype, new_n: usize) t: {
const Slice = @typeInfo(@TypeOf(old_mem)).pointer;
break :t []align(Slice.alignment) Slice.child;
} {
return self.allocator.realloc(old_mem, new_n) catch unreachable;
}
pub fn reallocAdvanced(
self: NeverFailingAllocator,
old_mem: anytype,
new_n: usize,
return_address: usize,
) t: {
const Slice = @typeInfo(@TypeOf(old_mem)).pointer;
break :t []align(Slice.alignment) Slice.child;
} {
return self.allocator.reallocAdvanced(old_mem, new_n, return_address) catch unreachable;
}
/// Free an array allocated with `alloc`.
/// If memory has length 0, free is a no-op.
/// To free a single item, see `destroy`.
pub fn free(self: NeverFailingAllocator, memory: anytype) void {
self.allocator.free(memory);
}
/// Copies `m` to newly allocated memory. Caller owns the memory.
pub fn dupe(self: NeverFailingAllocator, comptime T: type, m: []const T) []T {
return self.allocator.dupe(T, m) catch unreachable;
}
/// Copies `m` to newly allocated memory, with a null-terminated element. Caller owns the memory.
pub fn dupeZ(self: NeverFailingAllocator, comptime T: type, m: []const T) [:0]T {
return self.allocator.dupeZ(T, m) catch unreachable;
}
};
pub const NeverFailingArenaAllocator = struct { // MARK: NeverFailingArena
arena: std.heap.ArenaAllocator,
pub fn init(child_allocator: NeverFailingAllocator) NeverFailingArenaAllocator {
return .{
.arena = .init(child_allocator.allocator),
};
}
pub fn deinit(self: NeverFailingArenaAllocator) void {
self.arena.deinit();
}
pub fn allocator(self: *NeverFailingArenaAllocator) NeverFailingAllocator {
return .{
.allocator = self.arena.allocator(),
.IAssertThatTheProvidedAllocatorCantFail = {},
};
}
/// Resets the arena allocator and frees all allocated memory.
///
/// `mode` defines how the currently allocated memory is handled.
/// See the variant documentation for `ResetMode` for the effects of each mode.
///
/// The function will return whether the reset operation was successful or not.
/// If the reallocation failed `false` is returned. The arena will still be fully
/// functional in that case, all memory is released. Future allocations just might
/// be slower.
///
/// NOTE: If `mode` is `free_all`, the function will always return `true`.
pub fn reset(self: *NeverFailingArenaAllocator, mode: std.heap.ArenaAllocator.ResetMode) bool {
return self.arena.reset(mode);
}
pub fn shrinkAndFree(self: *NeverFailingArenaAllocator) void {
const node = self.arena.state.buffer_list.first orelse return;
const allocBuf = @as([*]u8, @ptrCast(node))[0..node.data];
const dataSize = std.mem.alignForward(usize, @sizeOf(std.SinglyLinkedList(usize).Node) + self.arena.state.end_index, @alignOf(std.SinglyLinkedList(usize).Node));
if(self.arena.child_allocator.rawResize(allocBuf, @enumFromInt(std.math.log2(@alignOf(std.SinglyLinkedList(usize).Node))), dataSize, @returnAddress())) {
node.data = dataSize;
}
}
};

View File

@ -2,7 +2,7 @@ const builtin = @import("builtin");
const std = @import("std");
const main = @import("../main.zig");
const NeverFailingAllocator = main.utils.NeverFailingAllocator;
const NeverFailingAllocator = main.heap.NeverFailingAllocator;
fn growCapacity(current: usize, minimum: usize) usize {
var new = current;

View File

@ -1,7 +1,7 @@
const std = @import("std");
const main = @import("main.zig");
const NeverFailingAllocator = main.utils.NeverFailingAllocator;
const NeverFailingAllocator = main.heap.NeverFailingAllocator;
const List = main.List;
pub const ZonElement = union(enum) { // MARK: Zon
@ -233,7 +233,7 @@ pub const ZonElement = union(enum) { // MARK: Zon
},
.vector => {
const len = @typeInfo(@TypeOf(value)).vector.len;
const result = initArray(main.utils.NeverFailingAllocator{.allocator = allocator, .IAssertThatTheProvidedAllocatorCantFail = {}});
const result = initArray(main.heap.NeverFailingAllocator{.allocator = allocator, .IAssertThatTheProvidedAllocatorCantFail = {}});
result.array.ensureCapacity(len);
inline for(0..len) |i| {
result.array.appendAssumeCapacity(createElementFromRandomType(value[i], allocator));