Improve the StackAllocator so it allows all kinds of allocation patterns.

This allows using the StackAllocator for more data structures and library functions.
Fixes #268
Fixes #95 (at least to the point where I can't do anything about it)
This commit is contained in:
IntegratedQuantum 2024-02-08 17:59:18 +01:00
parent 0177099f6b
commit d08096057f
15 changed files with 153 additions and 146 deletions

View File

@ -25,7 +25,7 @@ pub fn readAllJsonFilesInAddons(externalAllocator: NeverFailingAllocator, addons
}; };
defer dir.close(); defer dir.close();
var walker = dir.walk(main.globalAllocator.allocator) catch unreachable; var walker = dir.walk(main.stackAllocator.allocator) catch unreachable;
defer walker.deinit(); defer walker.deinit();
while(walker.next() catch |err| blk: { while(walker.next() catch |err| blk: {
@ -69,7 +69,7 @@ pub fn readAllFilesInAddons(externalAllocator: NeverFailingAllocator, addons: ma
}; };
defer dir.close(); defer dir.close();
var walker = dir.walk(main.globalAllocator.allocator) catch unreachable; var walker = dir.walk(main.stackAllocator.allocator) catch unreachable;
defer walker.deinit(); defer walker.deinit();
while(walker.next() catch |err| blk: { while(walker.next() catch |err| blk: {
@ -90,9 +90,9 @@ pub fn readAllFilesInAddons(externalAllocator: NeverFailingAllocator, addons: ma
} }
pub fn readAssets(externalAllocator: NeverFailingAllocator, assetPath: []const u8, blocks: *std.StringHashMap(JsonElement), items: *std.StringHashMap(JsonElement), biomes: *std.StringHashMap(JsonElement), recipes: *main.List([]const u8)) void { pub fn readAssets(externalAllocator: NeverFailingAllocator, assetPath: []const u8, blocks: *std.StringHashMap(JsonElement), items: *std.StringHashMap(JsonElement), biomes: *std.StringHashMap(JsonElement), recipes: *main.List([]const u8)) void {
var addons = main.List(std.fs.Dir).init(main.globalAllocator); var addons = main.List(std.fs.Dir).init(main.stackAllocator);
defer addons.deinit(); defer addons.deinit();
var addonNames = main.List([]const u8).init(main.globalAllocator); var addonNames = main.List([]const u8).init(main.stackAllocator);
defer addonNames.deinit(); defer addonNames.deinit();
{ // Find all the sub-directories to the assets folder. { // Find all the sub-directories to the assets folder.
@ -111,13 +111,13 @@ pub fn readAssets(externalAllocator: NeverFailingAllocator, assetPath: []const u
std.log.err("Got error while reading addon {s} from {s}: {s}", .{addon.name, assetPath, @errorName(err)}); std.log.err("Got error while reading addon {s} from {s}: {s}", .{addon.name, assetPath, @errorName(err)});
continue; continue;
}); });
addonNames.append(main.globalAllocator.dupe(u8, addon.name)); addonNames.append(main.stackAllocator.dupe(u8, addon.name));
} }
} }
} }
defer for(addons.items, addonNames.items) |*dir, addonName| { defer for(addons.items, addonNames.items) |*dir, addonName| {
dir.close(); dir.close();
main.globalAllocator.free(addonName); main.stackAllocator.free(addonName);
}; };
readAllJsonFilesInAddons(externalAllocator, addons, addonNames, "blocks", blocks); readAllJsonFilesInAddons(externalAllocator, addons, addonNames, "blocks", blocks);
@ -224,13 +224,13 @@ var loadedAssets: bool = false;
pub fn loadWorldAssets(assetFolder: []const u8, palette: *BlockPalette) !void { pub fn loadWorldAssets(assetFolder: []const u8, palette: *BlockPalette) !void {
if(loadedAssets) return; // The assets already got loaded by the server. if(loadedAssets) return; // The assets already got loaded by the server.
loadedAssets = true; loadedAssets = true;
var blocks = commonBlocks.cloneWithAllocator(main.globalAllocator.allocator) catch unreachable; var blocks = commonBlocks.cloneWithAllocator(main.stackAllocator.allocator) catch unreachable;
defer blocks.clearAndFree(); defer blocks.clearAndFree();
var items = commonItems.cloneWithAllocator(main.globalAllocator.allocator) catch unreachable; var items = commonItems.cloneWithAllocator(main.stackAllocator.allocator) catch unreachable;
defer items.clearAndFree(); defer items.clearAndFree();
var biomes = commonBiomes.cloneWithAllocator(main.globalAllocator.allocator) catch unreachable; var biomes = commonBiomes.cloneWithAllocator(main.stackAllocator.allocator) catch unreachable;
defer biomes.clearAndFree(); defer biomes.clearAndFree();
var recipes = main.List([]const u8).init(main.globalAllocator); var recipes = main.List([]const u8).init(main.stackAllocator);
recipes.appendSlice(commonRecipes.items); recipes.appendSlice(commonRecipes.items);
defer recipes.clearAndFree(); defer recipes.clearAndFree();

View File

@ -391,10 +391,8 @@ pub const draw = struct {
} }
pub inline fn print(comptime format: []const u8, args: anytype, x: f32, y: f32, fontSize: f32, alignment: TextBuffer.Alignment) void { pub inline fn print(comptime format: []const u8, args: anytype, x: f32, y: f32, fontSize: f32, alignment: TextBuffer.Alignment) void {
var stackFallback = std.heap.stackFallback(4096, main.globalAllocator.allocator); const string = std.fmt.allocPrint(main.stackAllocator.allocator, format, args) catch unreachable;
const allocator = stackFallback.get(); defer main.stackAllocator.free(string);
const string = std.fmt.allocPrint(allocator, format, args) catch unreachable;
defer allocator.free(string);
text(string, x, y ,fontSize, alignment); text(string, x, y ,fontSize, alignment);
} }
}; };
@ -560,13 +558,11 @@ pub const TextBuffer = struct {
pub fn init(allocator: NeverFailingAllocator, text: []const u8, initialFontEffect: FontEffect, showControlCharacters: bool, alignment: Alignment) TextBuffer { pub fn init(allocator: NeverFailingAllocator, text: []const u8, initialFontEffect: FontEffect, showControlCharacters: bool, alignment: Alignment) TextBuffer {
var self: TextBuffer = undefined; var self: TextBuffer = undefined;
self.alignment = alignment; self.alignment = alignment;
var stackFallback = std.heap.stackFallback(4096, main.globalAllocator.allocator);
const stackFallbackAllocator = stackFallback.get();
// Parse the input text: // Parse the input text:
var parser = Parser { var parser = Parser {
.unicodeIterator = std.unicode.Utf8Iterator{.bytes = text, .i = 0}, .unicodeIterator = std.unicode.Utf8Iterator{.bytes = text, .i = 0},
.currentFontEffect = initialFontEffect, .currentFontEffect = initialFontEffect,
.parsedText = main.List(u32).init(.{.allocator = stackFallbackAllocator, .IAssertThatTheProvidedAllocatorCantFail = {}}), .parsedText = main.List(u32).init(main.stackAllocator),
.fontEffects = main.List(FontEffect).init(allocator), .fontEffects = main.List(FontEffect).init(allocator),
.characterIndex = main.List(u32).init(allocator), .characterIndex = main.List(u32).init(allocator),
.showControlCharacters = showControlCharacters .showControlCharacters = showControlCharacters
@ -1040,9 +1036,7 @@ const TextRendering = struct {
} }
fn renderText(text: []const u8, x: f32, y: f32, fontSize: f32, initialFontEffect: TextBuffer.FontEffect, alignment: TextBuffer.Alignment) void { fn renderText(text: []const u8, x: f32, y: f32, fontSize: f32, initialFontEffect: TextBuffer.FontEffect, alignment: TextBuffer.Alignment) void {
var stackFallback = std.heap.stackFallback(4096, main.globalAllocator.allocator); const buf = TextBuffer.init(main.stackAllocator, text, initialFontEffect, false, alignment);
const allocator = stackFallback.get();
const buf = TextBuffer.init(.{.allocator = allocator, .IAssertThatTheProvidedAllocatorCantFail = {}}, text, initialFontEffect, false, alignment);
defer buf.deinit(); defer buf.deinit();
buf.render(x, y, fontSize); buf.render(x, y, fontSize);
@ -1530,7 +1524,7 @@ pub const TextureArray = struct {
const maxLOD = if(mipmapping) 1 + std.math.log2_int(u31, @min(maxWidth, maxHeight)) else 1; const maxLOD = if(mipmapping) 1 + std.math.log2_int(u31, @min(maxWidth, maxHeight)) else 1;
c.glTexStorage3D(c.GL_TEXTURE_2D_ARRAY, maxLOD, c.GL_RGBA8, maxWidth, maxHeight, @intCast(images.len)); c.glTexStorage3D(c.GL_TEXTURE_2D_ARRAY, maxLOD, c.GL_RGBA8, maxWidth, maxHeight, @intCast(images.len));
var arena = main.utils.NeverFailingArenaAllocator.init(main.globalAllocator); var arena = main.utils.NeverFailingArenaAllocator.init(main.stackAllocator);
defer arena.deinit(); defer arena.deinit();
const lodBuffer: [][]Color = arena.allocator().alloc([]Color, maxLOD); const lodBuffer: [][]Color = arena.allocator().alloc([]Color, maxLOD);
for(lodBuffer, 0..) |*buffer, i| { for(lodBuffer, 0..) |*buffer, i| {

View File

@ -182,12 +182,12 @@ pub fn deinit() void {
} }
fn save() void { fn save() void {
const guiJson = JsonElement.initObject(main.globalAllocator); const guiJson = JsonElement.initObject(main.stackAllocator);
defer guiJson.free(main.globalAllocator); defer guiJson.free(main.stackAllocator);
for(windowList.items) |window| { for(windowList.items) |window| {
const windowJson = JsonElement.initObject(main.globalAllocator); const windowJson = JsonElement.initObject(main.stackAllocator);
for(window.relativePosition, 0..) |relPos, i| { for(window.relativePosition, 0..) |relPos, i| {
const relPosJson = JsonElement.initObject(main.globalAllocator); const relPosJson = JsonElement.initObject(main.stackAllocator);
switch(relPos) { switch(relPos) {
.ratio => |ratio| { .ratio => |ratio| {
relPosJson.put("type", "ratio"); relPosJson.put("type", "ratio");
@ -222,13 +222,13 @@ fn save() void {
} }
fn load() void { fn load() void {
const json: JsonElement = main.files.readToJson(main.globalAllocator, "gui_layout.json") catch |err| blk: { const json: JsonElement = main.files.readToJson(main.stackAllocator, "gui_layout.json") catch |err| blk: {
if(err != error.FileNotFound) { if(err != error.FileNotFound) {
std.log.err("Could not read gui_layout.json: {s}", .{@errorName(err)}); std.log.err("Could not read gui_layout.json: {s}", .{@errorName(err)});
} }
break :blk JsonElement{.JsonNull={}}; break :blk JsonElement{.JsonNull={}};
}; };
defer json.free(main.globalAllocator); defer json.free(main.stackAllocator);
for(windowList.items) |window| { for(windowList.items) |window| {
const windowJson = json.getChild(window.id); const windowJson = json.getChild(window.id);
@ -649,7 +649,7 @@ pub const inventory = struct {
if(carriedItemStack.amount == 0) if(hoveredItemSlot) |hovered| { if(carriedItemStack.amount == 0) if(hoveredItemSlot) |hovered| {
if(hovered.itemStack.item) |item| { if(hovered.itemStack.item) |item| {
const tooltip = item.getTooltip(); const tooltip = item.getTooltip();
var textBuffer: graphics.TextBuffer = graphics.TextBuffer.init(main.globalAllocator, tooltip, .{}, false, .left); var textBuffer: graphics.TextBuffer = graphics.TextBuffer.init(main.stackAllocator, tooltip, .{}, false, .left);
defer textBuffer.deinit(); defer textBuffer.deinit();
var size = textBuffer.calculateLineBreaks(16, 256); var size = textBuffer.calculateLineBreaks(16, 256);
size[0] = 0; size[0] = 0;

View File

@ -275,10 +275,10 @@ pub const ItemDropManager = struct {
self.mutex.lock(); self.mutex.lock();
defer self.mutex.unlock(); defer self.mutex.unlock();
if(self.size == maxCapacity) { if(self.size == maxCapacity) {
const json = itemStack.store(main.globalAllocator); const json = itemStack.store(main.stackAllocator);
defer json.free(main.globalAllocator); defer json.free(main.stackAllocator);
const string = json.toString(main.stackAllocator); const string = json.toString(main.stackAllocator);
defer main.globalAllocator.free(string); defer main.stackAllocator.free(string);
std.log.err("Item drop capacitiy limit reached. Failed to add itemStack: {s}", .{string}); std.log.err("Item drop capacitiy limit reached. Failed to add itemStack: {s}", .{string});
if(itemStack.item) |item| { if(itemStack.item) |item| {
item.deinit(); item.deinit();

View File

@ -441,7 +441,7 @@ const TextureGenerator = struct {
var pixelMaterials: [16][16]PixelData = undefined; var pixelMaterials: [16][16]PixelData = undefined;
for(0..16) |x| { for(0..16) |x| {
for(0..16) |y| { for(0..16) |y| {
pixelMaterials[x][y] = PixelData.init(main.globalAllocator); pixelMaterials[x][y] = PixelData.init(main.stackAllocator);
} }
} }
@ -1331,19 +1331,19 @@ pub fn register(_: []const u8, texturePath: []const u8, replacementTexturePath:
} }
pub fn registerRecipes(file: []const u8) void { pub fn registerRecipes(file: []const u8) void {
var shortcuts = std.StringHashMap(*BaseItem).init(main.globalAllocator.allocator); var shortcuts = std.StringHashMap(*BaseItem).init(main.stackAllocator.allocator);
defer shortcuts.deinit(); defer shortcuts.deinit();
defer { defer {
var keyIterator = shortcuts.keyIterator(); var keyIterator = shortcuts.keyIterator();
while(keyIterator.next()) |key| { while(keyIterator.next()) |key| {
main.globalAllocator.free(key.*); main.stackAllocator.free(key.*);
} }
} }
var items = main.List(*BaseItem).init(main.globalAllocator); var items = main.List(*BaseItem).init(main.stackAllocator);
defer items.deinit(); defer items.deinit();
var itemAmounts = main.List(u16).init(main.globalAllocator); var itemAmounts = main.List(u16).init(main.stackAllocator);
defer itemAmounts.deinit(); defer itemAmounts.deinit();
var string = main.List(u8).init(main.globalAllocator); var string = main.List(u8).init(main.stackAllocator);
defer string.deinit(); defer string.deinit();
var lines = std.mem.split(u8, file, "\n"); var lines = std.mem.split(u8, file, "\n");
while(lines.next()) |line| { while(lines.next()) |line| {

View File

@ -97,7 +97,7 @@ const Socket = struct {
} }
fn resolveIP(addr: []const u8) !u32 { fn resolveIP(addr: []const u8) !u32 {
const list = try std.net.getAddressList(main.globalAllocator.allocator, addr, settings.defaultPort); const list = try std.net.getAddressList(main.stackAllocator.allocator, addr, settings.defaultPort);
defer list.deinit(); defer list.deinit();
return list.addrs[0].in.sa.addr; return list.addrs[0].in.sa.addr;
} }
@ -605,8 +605,8 @@ pub const Protocols = struct {
conn.handShakeState.store(data[0], .Monotonic); conn.handShakeState.store(data[0], .Monotonic);
switch(data[0]) { switch(data[0]) {
stepUserData => { stepUserData => {
const json = JsonElement.parseFromString(main.globalAllocator, data[1..]); const json = JsonElement.parseFromString(main.stackAllocator, data[1..]);
defer json.free(main.globalAllocator); defer json.free(main.stackAllocator);
const name = json.get([]const u8, "name", "unnamed"); const name = json.get([]const u8, "name", "unnamed");
const version = json.get([]const u8, "version", "unknown"); const version = json.get([]const u8, "version", "unknown");
std.log.info("User {s} joined using version {s}.", .{name, version}); std.log.info("User {s} joined using version {s}.", .{name, version});
@ -617,7 +617,7 @@ pub const Protocols = struct {
defer main.stackAllocator.free(path); defer main.stackAllocator.free(path);
var dir = try std.fs.cwd().openDir(path, .{.iterate = true}); var dir = try std.fs.cwd().openDir(path, .{.iterate = true});
defer dir.close(); defer dir.close();
var arrayList = main.List(u8).init(main.globalAllocator); var arrayList = main.List(u8).init(main.stackAllocator);
defer arrayList.deinit(); defer arrayList.deinit();
arrayList.append(stepAssets); arrayList.append(stepAssets);
try utils.Compression.pack(dir, arrayList.writer()); try utils.Compression.pack(dir, arrayList.writer());
@ -627,13 +627,13 @@ pub const Protocols = struct {
// TODO: // TODO:
conn.user.?.initPlayer(name); conn.user.?.initPlayer(name);
const jsonObject = JsonElement.initObject(main.globalAllocator); const jsonObject = JsonElement.initObject(main.stackAllocator);
defer jsonObject.free(main.globalAllocator); defer jsonObject.free(main.stackAllocator);
jsonObject.put("player", conn.user.?.player.save(main.globalAllocator)); jsonObject.put("player", conn.user.?.player.save(main.stackAllocator));
// TODO: // TODO:
// jsonObject.put("player_id", ((User)conn).player.id); // jsonObject.put("player_id", ((User)conn).player.id);
// jsonObject.put("blockPalette", Server.world.blockPalette.save()); // jsonObject.put("blockPalette", Server.world.blockPalette.save());
const spawn = JsonElement.initObject(main.globalAllocator); const spawn = JsonElement.initObject(main.stackAllocator);
spawn.put("x", main.server.world.?.spawn[0]); spawn.put("x", main.server.world.?.spawn[0]);
spawn.put("y", main.server.world.?.spawn[1]); spawn.put("y", main.server.world.?.spawn[1]);
spawn.put("z", main.server.world.?.spawn[2]); spawn.put("z", main.server.world.?.spawn[2]);
@ -657,8 +657,8 @@ pub const Protocols = struct {
try utils.Compression.unpack(dir, data[1..]); try utils.Compression.unpack(dir, data[1..]);
}, },
stepServerData => { stepServerData => {
const json = JsonElement.parseFromString(main.globalAllocator, data[1..]); const json = JsonElement.parseFromString(main.stackAllocator, data[1..]);
defer json.free(main.globalAllocator); defer json.free(main.stackAllocator);
try conn.manager.world.?.finishHandshake(json); try conn.manager.world.?.finishHandshake(json);
conn.handShakeState.store(stepComplete, .Monotonic); conn.handShakeState.store(stepComplete, .Monotonic);
conn.handShakeWaiting.broadcast(); // Notify the waiting client thread. conn.handShakeWaiting.broadcast(); // Notify the waiting client thread.
@ -680,8 +680,8 @@ pub const Protocols = struct {
} }
pub fn clientSide(conn: *Connection, name: []const u8) void { pub fn clientSide(conn: *Connection, name: []const u8) void {
const jsonObject = JsonElement.initObject(main.globalAllocator); const jsonObject = JsonElement.initObject(main.stackAllocator);
defer jsonObject.free(main.globalAllocator); defer jsonObject.free(main.stackAllocator);
jsonObject.putOwnedString("version", settings.version); jsonObject.putOwnedString("version", settings.version);
jsonObject.putOwnedString("name", name); jsonObject.putOwnedString("name", name);
const prefix = [1]u8 {stepUserData}; const prefix = [1]u8 {stepUserData};
@ -869,8 +869,8 @@ pub const Protocols = struct {
pub const entity = struct { pub const entity = struct {
pub const id: u8 = 8; pub const id: u8 = 8;
fn receive(conn: *Connection, data: []const u8) !void { fn receive(conn: *Connection, data: []const u8) !void {
const jsonArray = JsonElement.parseFromString(main.globalAllocator, data); const jsonArray = JsonElement.parseFromString(main.stackAllocator, data);
defer jsonArray.free(main.globalAllocator); defer jsonArray.free(main.stackAllocator);
var i: u32 = 0; var i: u32 = 0;
while(i < jsonArray.JsonArray.items.len) : (i += 1) { while(i < jsonArray.JsonArray.items.len) : (i += 1) {
const elem = jsonArray.JsonArray.items[i]; const elem = jsonArray.JsonArray.items[i];
@ -1065,8 +1065,8 @@ pub const Protocols = struct {
// ); // );
}, },
type_itemStackCollect => { type_itemStackCollect => {
const json = JsonElement.parseFromString(main.globalAllocator, data[1..]); const json = JsonElement.parseFromString(main.stackAllocator, data[1..]);
defer json.free(main.globalAllocator); defer json.free(main.stackAllocator);
const item = items.Item.init(json) catch |err| { const item = items.Item.init(json) catch |err| {
std.log.err("Error {s} while collecting item {s}. Ignoring it.", .{@errorName(err), data[1..]}); std.log.err("Error {s} while collecting item {s}. Ignoring it.", .{@errorName(err), data[1..]});
return; return;
@ -1083,8 +1083,8 @@ pub const Protocols = struct {
}, },
type_timeAndBiome => { type_timeAndBiome => {
if(conn.manager.world) |world| { if(conn.manager.world) |world| {
const json = JsonElement.parseFromString(main.globalAllocator, data[1..]); const json = JsonElement.parseFromString(main.stackAllocator, data[1..]);
defer json.free(main.globalAllocator); defer json.free(main.stackAllocator);
const expectedTime = json.get(i64, "time", 0); const expectedTime = json.get(i64, "time", 0);
var curTime = world.gameTime.load(.Monotonic); var curTime = world.gameTime.load(.Monotonic);
if(@abs(curTime -% expectedTime) >= 1000) { if(@abs(curTime -% expectedTime) >= 1000) {
@ -1155,8 +1155,8 @@ pub const Protocols = struct {
pub fn sendInventory_full(conn: *Connection, inv: Inventory) void { pub fn sendInventory_full(conn: *Connection, inv: Inventory) void {
const json = inv.save(main.globalAllocator); const json = inv.save(main.stackAllocator);
defer json.free(main.globalAllocator); defer json.free(main.stackAllocator);
const string = json.toString(main.stackAllocator); const string = json.toString(main.stackAllocator);
defer main.stackAllocator.free(string); defer main.stackAllocator.free(string);
addHeaderAndSendImportant(conn, type_inventoryFull, string); addHeaderAndSendImportant(conn, type_inventoryFull, string);
@ -1169,8 +1169,8 @@ pub const Protocols = struct {
} }
pub fn itemStackDrop(conn: *Connection, stack: ItemStack, pos: Vec3d, dir: Vec3f, vel: f32) void { pub fn itemStackDrop(conn: *Connection, stack: ItemStack, pos: Vec3d, dir: Vec3f, vel: f32) void {
const jsonObject = stack.store(main.globalAllocator); const jsonObject = stack.store(main.stackAllocator);
defer jsonObject.free(main.globalAllocator); defer jsonObject.free(main.stackAllocator);
jsonObject.put("x", pos[0]); jsonObject.put("x", pos[0]);
jsonObject.put("y", pos[1]); jsonObject.put("y", pos[1]);
jsonObject.put("z", pos[2]); jsonObject.put("z", pos[2]);
@ -1184,16 +1184,16 @@ pub const Protocols = struct {
} }
pub fn itemStackCollect(conn: *Connection, stack: ItemStack) void { pub fn itemStackCollect(conn: *Connection, stack: ItemStack) void {
const json = stack.store(main.globalAllocator); const json = stack.store(main.stackAllocator);
defer json.free(main.globalAllocator); defer json.free(main.stackAllocator);
const string = json.toString(main.stackAllocator); const string = json.toString(main.stackAllocator);
defer main.stackAllocator.free(string); defer main.stackAllocator.free(string);
addHeaderAndSendImportant(conn, type_itemStackCollect, string); addHeaderAndSendImportant(conn, type_itemStackCollect, string);
} }
pub fn sendTimeAndBiome(conn: *Connection, world: *const main.server.ServerWorld) void { pub fn sendTimeAndBiome(conn: *Connection, world: *const main.server.ServerWorld) void {
const json = JsonElement.initObject(main.globalAllocator); const json = JsonElement.initObject(main.stackAllocator);
defer json.free(main.globalAllocator); defer json.free(main.stackAllocator);
json.put("time", world.gameTime); json.put("time", world.gameTime);
const pos = conn.user.?.player.pos; const pos = conn.user.?.player.pos;
json.put("biome", (world.getBiome(@intFromFloat(pos[0]), @intFromFloat(pos[1]), @intFromFloat(pos[2]))).id); json.put("biome", (world.getBiome(@intFromFloat(pos[0]), @intFromFloat(pos[1]), @intFromFloat(pos[2]))).id);
@ -1483,9 +1483,9 @@ pub const Connection = struct {
self.mutex.lock(); self.mutex.lock();
defer self.mutex.unlock(); defer self.mutex.unlock();
var runLengthEncodingStarts: main.List(u32) = main.List(u32).init(main.globalAllocator); var runLengthEncodingStarts: main.List(u32) = main.List(u32).init(main.stackAllocator);
defer runLengthEncodingStarts.deinit(); defer runLengthEncodingStarts.deinit();
var runLengthEncodingLengths: main.List(u32) = main.List(u32).init(main.globalAllocator); var runLengthEncodingLengths: main.List(u32) = main.List(u32).init(main.stackAllocator);
defer runLengthEncodingLengths.deinit(); defer runLengthEncodingLengths.deinit();
for(self.receivedPackets) |list| { for(self.receivedPackets) |list| {

View File

@ -486,19 +486,19 @@ pub const MenuBackGround = struct {
var dir = try std.fs.cwd().makeOpenPath("assets/backgrounds", .{.iterate = true}); var dir = try std.fs.cwd().makeOpenPath("assets/backgrounds", .{.iterate = true});
defer dir.close(); defer dir.close();
var walker = try dir.walk(main.globalAllocator.allocator); var walker = try dir.walk(main.stackAllocator.allocator);
defer walker.deinit(); defer walker.deinit();
var fileList = main.List([]const u8).init(main.globalAllocator); var fileList = main.List([]const u8).init(main.stackAllocator);
defer { defer {
for(fileList.items) |fileName| { for(fileList.items) |fileName| {
main.globalAllocator.free(fileName); main.stackAllocator.free(fileName);
} }
fileList.deinit(); fileList.deinit();
} }
while(try walker.next()) |entry| { while(try walker.next()) |entry| {
if(entry.kind == .file and std.ascii.endsWithIgnoreCase(entry.basename, ".png")) { if(entry.kind == .file and std.ascii.endsWithIgnoreCase(entry.basename, ".png")) {
fileList.append(main.globalAllocator.dupe(u8, entry.path)); fileList.append(main.stackAllocator.dupe(u8, entry.path));
} }
} }
if(fileList.items.len == 0) { if(fileList.items.len == 0) {

View File

@ -574,7 +574,7 @@ pub const ChunkMesh = struct {
fn initLight(self: *ChunkMesh) void { fn initLight(self: *ChunkMesh) void {
self.mutex.lock(); self.mutex.lock();
var lightEmittingBlocks = main.List([3]u8).init(main.globalAllocator); var lightEmittingBlocks = main.List([3]u8).init(main.stackAllocator);
defer lightEmittingBlocks.deinit(); defer lightEmittingBlocks.deinit();
var x: u8 = 0; var x: u8 = 0;
while(x < chunk.chunkSize): (x += 1) { while(x < chunk.chunkSize): (x += 1) {

View File

@ -88,7 +88,7 @@ pub const ChannelChunk = struct {
var neighborLists: [6]main.ListUnmanaged(Entry) = .{.{}} ** 6; var neighborLists: [6]main.ListUnmanaged(Entry) = .{.{}} ** 6;
defer { defer {
for(&neighborLists) |*list| { for(&neighborLists) |*list| {
list.deinit(lightQueue.allocator); list.deinit(main.stackAllocator);
} }
} }
@ -108,7 +108,7 @@ pub const ChannelChunk = struct {
} }
if(result.value == 0) continue; if(result.value == 0) continue;
if(nx < 0 or nx >= chunk.chunkSize or ny < 0 or ny >= chunk.chunkSize or nz < 0 or nz >= chunk.chunkSize) { if(nx < 0 or nx >= chunk.chunkSize or ny < 0 or ny >= chunk.chunkSize or nz < 0 or nz >= chunk.chunkSize) {
neighborLists[neighbor].append(lightQueue.allocator, result); neighborLists[neighbor].append(main.stackAllocator, result);
continue; continue;
} }
const neighborIndex = chunk.getIndex(nx, ny, nz); const neighborIndex = chunk.getIndex(nx, ny, nz);
@ -136,7 +136,7 @@ pub const ChannelChunk = struct {
var constructiveList: main.ListUnmanaged(PositionEntry) = .{}; var constructiveList: main.ListUnmanaged(PositionEntry) = .{};
defer { defer {
for(&neighborLists) |*list| { for(&neighborLists) |*list| {
list.deinit(lightQueue.allocator); list.deinit(main.stackAllocator);
} }
} }
var isFirstIteration: bool = isFirstBlock; var isFirstIteration: bool = isFirstBlock;
@ -146,7 +146,7 @@ pub const ChannelChunk = struct {
const index = chunk.getIndex(entry.x, entry.y, entry.z); const index = chunk.getIndex(entry.x, entry.y, entry.z);
if(entry.value != self.data[index].load(.Unordered)) { if(entry.value != self.data[index].load(.Unordered)) {
if(self.data[index].load(.Unordered) != 0) { if(self.data[index].load(.Unordered) != 0) {
constructiveList.append(lightQueue.allocator, .{.x = entry.x, .y = entry.y, .z = entry.z}); constructiveList.append(main.stackAllocator, .{.x = entry.x, .y = entry.y, .z = entry.z});
} }
continue; continue;
} }
@ -163,7 +163,7 @@ pub const ChannelChunk = struct {
result.value -|= 8*|@as(u8, @intCast(self.ch.pos.voxelSize)); result.value -|= 8*|@as(u8, @intCast(self.ch.pos.voxelSize));
} }
if(nx < 0 or nx >= chunk.chunkSize or ny < 0 or ny >= chunk.chunkSize or nz < 0 or nz >= chunk.chunkSize) { if(nx < 0 or nx >= chunk.chunkSize or ny < 0 or ny >= chunk.chunkSize or nz < 0 or nz >= chunk.chunkSize) {
neighborLists[neighbor].append(lightQueue.allocator, result); neighborLists[neighbor].append(main.stackAllocator, result);
continue; continue;
} }
const neighborIndex = chunk.getIndex(nx, ny, nz); const neighborIndex = chunk.getIndex(nx, ny, nz);
@ -181,7 +181,7 @@ pub const ChannelChunk = struct {
for(0..6) |neighbor| { for(0..6) |neighbor| {
if(neighborLists[neighbor].items.len == 0) continue; if(neighborLists[neighbor].items.len == 0) continue;
const neighborMesh = mesh_storage.getNeighborAndIncreaseRefCount(self.ch.pos, self.ch.pos.voxelSize, @intCast(neighbor)) orelse continue; const neighborMesh = mesh_storage.getNeighborAndIncreaseRefCount(self.ch.pos, self.ch.pos.voxelSize, @intCast(neighbor)) orelse continue;
constructiveEntries.append(lightQueue.allocator, .{ constructiveEntries.append(main.stackAllocator, .{
.mesh = neighborMesh, .mesh = neighborMesh,
.entries = neighborMesh.lightingData[@intFromEnum(self.channel)].propagateDestructiveFromNeighbor(lightQueue, neighborLists[neighbor].items, constructiveEntries), .entries = neighborMesh.lightingData[@intFromEnum(self.channel)].propagateDestructiveFromNeighbor(lightQueue, neighborLists[neighbor].items, constructiveEntries),
}); });
@ -217,12 +217,7 @@ pub const ChannelChunk = struct {
} }
pub fn propagateLights(self: *ChannelChunk, lights: []const [3]u8, comptime checkNeighbors: bool) void { pub fn propagateLights(self: *ChannelChunk, lights: []const [3]u8, comptime checkNeighbors: bool) void {
const buf = main.stackAllocator.alloc(u8, 1 << 16); var lightQueue = main.utils.CircularBufferQueue(Entry).init(main.stackAllocator, 1 << 12);
defer main.stackAllocator.free(buf);
var bufferFallback = main.utils.BufferFallbackAllocator.init(buf, main.globalAllocator);
var arena = main.utils.NeverFailingArenaAllocator.init(bufferFallback.allocator());
defer arena.deinit();
var lightQueue = main.utils.CircularBufferQueue(Entry).init(arena.allocator(), 1 << 10);
defer lightQueue.deinit(); defer lightQueue.deinit();
for(lights) |pos| { for(lights) |pos| {
const index = chunk.getIndex(pos[0], pos[1], pos[2]); const index = chunk.getIndex(pos[0], pos[1], pos[2]);
@ -280,20 +275,15 @@ pub const ChannelChunk = struct {
} }
pub fn propagateLightsDestructive(self: *ChannelChunk, lights: []const [3]u8) void { pub fn propagateLightsDestructive(self: *ChannelChunk, lights: []const [3]u8) void {
const buf = main.stackAllocator.alloc(u8, 1 << 16); var lightQueue = main.utils.CircularBufferQueue(Entry).init(main.stackAllocator, 1 << 12);
defer main.stackAllocator.free(buf);
var bufferFallback = main.utils.BufferFallbackAllocator.init(buf, main.globalAllocator);
var arena = main.utils.NeverFailingArenaAllocator.init(bufferFallback.allocator());
defer arena.deinit();
var lightQueue = main.utils.CircularBufferQueue(Entry).init(arena.allocator(), 1 << 10);
defer lightQueue.deinit(); defer lightQueue.deinit();
for(lights) |pos| { for(lights) |pos| {
const index = chunk.getIndex(pos[0], pos[1], pos[2]); const index = chunk.getIndex(pos[0], pos[1], pos[2]);
lightQueue.enqueue(.{.x = @intCast(pos[0]), .y = @intCast(pos[1]), .z = @intCast(pos[2]), .value = self.data[index].load(.Unordered), .sourceDir = 6}); lightQueue.enqueue(.{.x = @intCast(pos[0]), .y = @intCast(pos[1]), .z = @intCast(pos[2]), .value = self.data[index].load(.Unordered), .sourceDir = 6});
} }
var constructiveEntries: main.ListUnmanaged(ChunkEntries) = .{}; var constructiveEntries: main.ListUnmanaged(ChunkEntries) = .{};
defer constructiveEntries.deinit(arena.allocator()); defer constructiveEntries.deinit(main.stackAllocator);
constructiveEntries.append(arena.allocator(), .{ constructiveEntries.append(main.stackAllocator, .{
.mesh = null, .mesh = null,
.entries = self.propagateDestructive(&lightQueue, &constructiveEntries, true), .entries = self.propagateDestructive(&lightQueue, &constructiveEntries, true),
}); });
@ -301,7 +291,7 @@ pub const ChannelChunk = struct {
const mesh = entries.mesh; const mesh = entries.mesh;
defer if(mesh) |_mesh| _mesh.decreaseRefCount(); defer if(mesh) |_mesh| _mesh.decreaseRefCount();
var entryList = entries.entries; var entryList = entries.entries;
defer entryList.deinit(arena.allocator()); defer entryList.deinit(main.stackAllocator);
const channelChunk = if(mesh) |_mesh| _mesh.lightingData[@intFromEnum(self.channel)] else self; const channelChunk = if(mesh) |_mesh| _mesh.lightingData[@intFromEnum(self.channel)] else self;
for(entryList.items) |entry| { for(entryList.items) |entry| {
const index = chunk.getIndex(entry.x, entry.y, entry.z); const index = chunk.getIndex(entry.x, entry.y, entry.z);

View File

@ -531,9 +531,9 @@ pub noinline fn updateAndGetRenderChunks(conn: *network.Connection, playerPos: V
network.Protocols.genericUpdate.sendRenderDistance(conn, renderDistance); network.Protocols.genericUpdate.sendRenderDistance(conn, renderDistance);
} }
var meshRequests = main.List(chunk.ChunkPosition).init(main.globalAllocator); var meshRequests = main.List(chunk.ChunkPosition).init(main.stackAllocator);
defer meshRequests.deinit(); defer meshRequests.deinit();
var mapRequests = main.List(LightMap.MapFragmentPosition).init(main.globalAllocator); var mapRequests = main.List(LightMap.MapFragmentPosition).init(main.stackAllocator);
defer mapRequests.deinit(); defer mapRequests.deinit();
const olderPx = lastPx; const olderPx = lastPx;
@ -568,7 +568,7 @@ pub noinline fn updateAndGetRenderChunks(conn: *network.Connection, playerPos: V
}; };
// TODO: Is there a way to combine this with minecraft's approach? // TODO: Is there a way to combine this with minecraft's approach?
var searchList = std.PriorityQueue(OcclusionData, void, OcclusionData.compare).init(main.globalAllocator.allocator, {}); var searchList = std.PriorityQueue(OcclusionData, void, OcclusionData.compare).init(main.stackAllocator.allocator, {});
defer searchList.deinit(); defer searchList.deinit();
{ {
var firstPos = chunk.ChunkPosition{ var firstPos = chunk.ChunkPosition{
@ -601,7 +601,7 @@ pub noinline fn updateAndGetRenderChunks(conn: *network.Connection, playerPos: V
firstPos.voxelSize *= 2; firstPos.voxelSize *= 2;
} }
} }
var nodeList = main.List(*ChunkMeshNode).init(main.globalAllocator); var nodeList = main.List(*ChunkMeshNode).init(main.stackAllocator);
defer nodeList.deinit(); defer nodeList.deinit();
const projRotMat = game.projectionMatrix.mul(game.camera.viewMatrix); const projRotMat = game.projectionMatrix.mul(game.camera.viewMatrix);
while(searchList.removeOrNull()) |data| { while(searchList.removeOrNull()) |data| {

View File

@ -146,10 +146,10 @@ pub const Biome = struct {
const structures = json.getChild("structures"); const structures = json.getChild("structures");
var vegetation = main.ListUnmanaged(StructureModel){}; var vegetation = main.ListUnmanaged(StructureModel){};
defer vegetation.deinit(main.globalAllocator); defer vegetation.deinit(main.stackAllocator);
for(structures.toSlice()) |elem| { for(structures.toSlice()) |elem| {
if(StructureModel.initModel(elem)) |model| { if(StructureModel.initModel(elem)) |model| {
vegetation.append(main.globalAllocator, model); vegetation.append(main.stackAllocator, model);
} }
} }
self.vegetationModels = main.globalAllocator.dupe(StructureModel, vegetation.items); self.vegetationModels = main.globalAllocator.dupe(StructureModel, vegetation.items);

View File

@ -30,7 +30,7 @@ pub fn deinit() void {
pub fn generateMapFragment(map: *ClimateMapFragment, worldSeed: u64) void { pub fn generateMapFragment(map: *ClimateMapFragment, worldSeed: u64) void {
var seed: u64 = worldSeed; var seed: u64 = worldSeed;
const generator = GenerationStructure.init(main.globalAllocator, map.pos.wx, map.pos.wz, ClimateMapFragment.mapSize, ClimateMapFragment.mapSize, terrain.biomes.byTypeBiomes, seed); const generator = GenerationStructure.init(main.stackAllocator, map.pos.wx, map.pos.wz, ClimateMapFragment.mapSize, ClimateMapFragment.mapSize, terrain.biomes.byTypeBiomes, seed);
defer generator.deinit(main.stackAllocator); defer generator.deinit(main.stackAllocator);
generator.toMap(map, ClimateMapFragment.mapSize, ClimateMapFragment.mapSize, worldSeed); generator.toMap(map, ClimateMapFragment.mapSize, ClimateMapFragment.mapSize, worldSeed);
@ -327,7 +327,7 @@ const GenerationStructure = struct {
} }
// Add some sub-biomes: // Add some sub-biomes:
var extraBiomes = main.List(BiomePoint).init(main.globalAllocator); var extraBiomes = main.List(BiomePoint).init(main.stackAllocator);
defer extraBiomes.deinit(); defer extraBiomes.deinit();
for(self.chunks.mem) |chunk| { for(self.chunks.mem) |chunk| {
for(chunk.biomesSortedByX) |biome| { for(chunk.biomesSortedByX) |biome| {

View File

@ -269,8 +269,8 @@ const WorldIO = struct {
/// Load the seed, which is needed before custom item and ore generation. /// Load the seed, which is needed before custom item and ore generation.
pub fn loadWorldSeed(self: WorldIO) !u64 { pub fn loadWorldSeed(self: WorldIO) !u64 {
const worldData: JsonElement = try self.dir.readToJson(main.globalAllocator, "world.dat"); const worldData: JsonElement = try self.dir.readToJson(main.stackAllocator, "world.dat");
defer worldData.free(main.globalAllocator); defer worldData.free(main.stackAllocator);
if(worldData.get(u32, "version", 0) != worldDataVersion) { if(worldData.get(u32, "version", 0) != worldDataVersion) {
std.log.err("Cannot read world file version {}. Expected version {}.", .{worldData.get(u32, "version", 0), worldDataVersion}); std.log.err("Cannot read world file version {}. Expected version {}.", .{worldData.get(u32, "version", 0), worldDataVersion});
return error.OldWorld; return error.OldWorld;
@ -279,8 +279,8 @@ const WorldIO = struct {
} }
pub fn loadWorldData(self: WorldIO) !void { pub fn loadWorldData(self: WorldIO) !void {
const worldData: JsonElement = try self.dir.readToJson(main.globalAllocator, "world.dat"); const worldData: JsonElement = try self.dir.readToJson(main.stackAllocator, "world.dat");
defer worldData.free(main.globalAllocator); defer worldData.free(main.stackAllocator);
const entityJson = worldData.getChild("entities"); const entityJson = worldData.getChild("entities");
_ = entityJson; _ = entityJson;
@ -300,15 +300,15 @@ const WorldIO = struct {
} }
pub fn saveWorldData(self: WorldIO) !void { pub fn saveWorldData(self: WorldIO) !void {
const worldData: JsonElement = JsonElement.initObject(main.globalAllocator); const worldData: JsonElement = JsonElement.initObject(main.stackAllocator);
defer worldData.free(main.globalAllocator); defer worldData.free(main.stackAllocator);
worldData.put("version", worldDataVersion); worldData.put("version", worldDataVersion);
worldData.put("seed", self.world.seed); worldData.put("seed", self.world.seed);
worldData.put("doGameTimeCycle", self.world.doGameTimeCycle); worldData.put("doGameTimeCycle", self.world.doGameTimeCycle);
worldData.put("gameTime", self.world.gameTime); worldData.put("gameTime", self.world.gameTime);
// TODO: // TODO:
// worldData.put("entityCount", world.getEntities().length); // worldData.put("entityCount", world.getEntities().length);
const spawnData = JsonElement.initObject(main.globalAllocator); const spawnData = JsonElement.initObject(main.stackAllocator);
spawnData.put("x", self.world.spawn[0]); spawnData.put("x", self.world.spawn[0]);
spawnData.put("y", self.world.spawn[1]); spawnData.put("y", self.world.spawn[1]);
spawnData.put("z", self.world.spawn[2]); spawnData.put("z", self.world.spawn[2]);
@ -366,7 +366,7 @@ pub const ServerWorld = struct {
self.itemDropManager.init(main.globalAllocator, self, self.gravity); self.itemDropManager.init(main.globalAllocator, self, self.gravity);
errdefer self.itemDropManager.deinit(); errdefer self.itemDropManager.deinit();
var loadArena = main.utils.NeverFailingArenaAllocator.init(main.globalAllocator); var loadArena = main.utils.NeverFailingArenaAllocator.init(main.stackAllocator);
defer loadArena.deinit(); defer loadArena.deinit();
const arenaAllocator = loadArena.allocator(); const arenaAllocator = loadArena.allocator();
var buf: [32768]u8 = undefined; var buf: [32768]u8 = undefined;
@ -438,8 +438,8 @@ pub const ServerWorld = struct {
pub fn findPlayer(self: *ServerWorld, user: *User) void { pub fn findPlayer(self: *ServerWorld, user: *User) void {
var buf: [1024]u8 = undefined; var buf: [1024]u8 = undefined;
const playerData = files.readToJson(main.globalAllocator, std.fmt.bufPrint(&buf, "saves/{s}/player/{s}.json", .{self.name, user.name}) catch "") catch .JsonNull; // TODO: Utils.escapeFolderName(user.name) const playerData = files.readToJson(main.stackAllocator, std.fmt.bufPrint(&buf, "saves/{s}/player/{s}.json", .{self.name, user.name}) catch "") catch .JsonNull; // TODO: Utils.escapeFolderName(user.name)
defer playerData.free(main.globalAllocator); defer playerData.free(main.stackAllocator);
const player = &user.player; const player = &user.player;
if(playerData == .JsonNull) { if(playerData == .JsonNull) {
// Generate a new player: // Generate a new player:
@ -473,8 +473,8 @@ pub const ServerWorld = struct {
// savePlayers(); // savePlayers();
// chunkManager.forceSave(); // chunkManager.forceSave();
// ChunkIO.save(); // ChunkIO.save();
const itemDropJson = self.itemDropManager.store(main.globalAllocator); const itemDropJson = self.itemDropManager.store(main.stackAllocator);
defer itemDropJson.free(main.globalAllocator); defer itemDropJson.free(main.stackAllocator);
var buf: [32768]u8 = undefined; var buf: [32768]u8 = undefined;
try files.writeJson(try std.fmt.bufPrint(&buf, "saves/{s}/items.json", .{self.name}), itemDropJson); try files.writeJson(try std.fmt.bufPrint(&buf, "saves/{s}/items.json", .{self.name}), itemDropJson);
} }

View File

@ -41,13 +41,13 @@ pub var developerAutoEnterWorld: []const u8 = "";
pub fn init() void { pub fn init() void {
const json: JsonElement = main.files.readToJson(main.globalAllocator, "settings.json") catch |err| { const json: JsonElement = main.files.readToJson(main.stackAllocator, "settings.json") catch |err| {
if(err != error.FileNotFound) { if(err != error.FileNotFound) {
std.log.err("Could not read settings file: {s}", .{@errorName(err)}); std.log.err("Could not read settings file: {s}", .{@errorName(err)});
} }
return; return;
}; };
defer json.free(main.globalAllocator); defer json.free(main.stackAllocator);
inline for(@typeInfo(@This()).Struct.decls) |decl| { inline for(@typeInfo(@This()).Struct.decls) |decl| {
const is_const = @typeInfo(@TypeOf(&@field(@This(), decl.name))).Pointer.is_const; // Sadly there is no direct way to check if a declaration is const. const is_const = @typeInfo(@TypeOf(&@field(@This(), decl.name))).Pointer.is_const; // Sadly there is no direct way to check if a declaration is const.
@ -78,8 +78,8 @@ pub fn init() void {
} }
pub fn deinit() void { pub fn deinit() void {
const jsonObject = JsonElement.initObject(main.globalAllocator); const jsonObject = JsonElement.initObject(main.stackAllocator);
defer jsonObject.free(main.globalAllocator); defer jsonObject.free(main.stackAllocator);
inline for(@typeInfo(@This()).Struct.decls) |decl| { inline for(@typeInfo(@This()).Struct.decls) |decl| {
const is_const = @typeInfo(@TypeOf(&@field(@This(), decl.name))).Pointer.is_const; // Sadly there is no direct way to check if a declaration is const. const is_const = @typeInfo(@TypeOf(&@field(@This(), decl.name))).Pointer.is_const; // Sadly there is no direct way to check if a declaration is const.
@ -104,9 +104,9 @@ pub fn deinit() void {
} }
// keyboard settings: // keyboard settings:
const keyboard = JsonElement.initObject(main.globalAllocator); const keyboard = JsonElement.initObject(main.stackAllocator);
for(&main.KeyBoard.keys) |key| { for(&main.KeyBoard.keys) |key| {
const keyJson = JsonElement.initObject(main.globalAllocator); const keyJson = JsonElement.initObject(main.stackAllocator);
keyJson.put("key", key.key); keyJson.put("key", key.key);
keyJson.put("mouseButton", key.mouseButton); keyJson.put("mouseButton", key.mouseButton);
keyJson.put("scancode", key.scancode); keyJson.put("scancode", key.scancode);

View File

@ -8,7 +8,7 @@ const main = @import("main.zig");
pub const Compression = struct { pub const Compression = struct {
pub fn deflate(allocator: NeverFailingAllocator, data: []const u8) []u8 { pub fn deflate(allocator: NeverFailingAllocator, data: []const u8) []u8 {
var result = main.List(u8).init(allocator); var result = main.List(u8).init(allocator);
var comp = std.compress.deflate.compressor(main.globalAllocator.allocator, result.writer(), .{.level = .default_compression}) catch unreachable; var comp = std.compress.deflate.compressor(main.stackAllocator.allocator, result.writer(), .{.level = .default_compression}) catch unreachable;
_ = comp.write(data) catch unreachable; _ = comp.write(data) catch unreachable;
comp.close() catch unreachable; comp.close() catch unreachable;
comp.deinit(); comp.deinit();
@ -25,9 +25,9 @@ pub const Compression = struct {
} }
pub fn pack(sourceDir: std.fs.Dir, writer: anytype) !void { pub fn pack(sourceDir: std.fs.Dir, writer: anytype) !void {
var comp = try std.compress.deflate.compressor(main.globalAllocator.allocator, writer, .{.level = .default_compression}); var comp = try std.compress.deflate.compressor(main.stackAllocator.allocator, writer, .{.level = .default_compression});
defer comp.deinit(); defer comp.deinit();
var walker = try sourceDir.walk(main.globalAllocator.allocator); var walker = try sourceDir.walk(main.stackAllocator.allocator);
defer walker.deinit(); defer walker.deinit();
while(try walker.next()) |entry| { while(try walker.next()) |entry| {
@ -61,7 +61,7 @@ pub const Compression = struct {
pub fn unpack(outDir: std.fs.Dir, input: []const u8) !void { pub fn unpack(outDir: std.fs.Dir, input: []const u8) !void {
var stream = std.io.fixedBufferStream(input); var stream = std.io.fixedBufferStream(input);
var decomp = try std.compress.deflate.decompressor(main.globalAllocator.allocator, stream.reader(), null); var decomp = try std.compress.deflate.decompressor(main.stackAllocator.allocator, stream.reader(), null);
defer decomp.deinit(); defer decomp.deinit();
const reader = decomp.reader(); const reader = decomp.reader();
const _data = try reader.readAllAlloc(main.stackAllocator.allocator, std.math.maxInt(usize)); const _data = try reader.readAllAlloc(main.stackAllocator.allocator, std.math.maxInt(usize));
@ -373,26 +373,23 @@ pub fn CircularBufferQueue(comptime T: type) type {
/// Allows for stack-like allocations in a fast and safe way. /// 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. /// It is safe in the sense that a regular allocator will be used when the buffer is full.
pub const StackAllocator = struct { pub const StackAllocator = struct {
const Allocation = struct{start: u32, len: u32}; const AllocationTrailer = packed struct{wasFreed: bool, previousAllocationTrailer: u31};
backingAllocator: NeverFailingAllocator, backingAllocator: NeverFailingAllocator,
buffer: []align(4096) u8, buffer: []align(4096) u8,
allocationList: main.List(Allocation),
index: usize, index: usize,
pub fn init(backingAllocator: NeverFailingAllocator, size: u32) StackAllocator { pub fn init(backingAllocator: NeverFailingAllocator, size: u31) StackAllocator {
return .{ return .{
.backingAllocator = backingAllocator, .backingAllocator = backingAllocator,
.buffer = backingAllocator.alignedAlloc(u8, 4096, size), .buffer = backingAllocator.alignedAlloc(u8, 4096, size),
.allocationList = main.List(Allocation).init(backingAllocator),
.index = 0, .index = 0,
}; };
} }
pub fn deinit(self: StackAllocator) void { pub fn deinit(self: StackAllocator) void {
if(self.allocationList.items.len != 0) { if(self.index != 0) {
std.log.err("Memory leak in Stack Allocator", .{}); std.log.err("Memory leak in Stack Allocator", .{});
} }
self.allocationList.deinit();
self.backingAllocator.free(self.buffer); self.backingAllocator.free(self.buffer);
} }
@ -423,6 +420,16 @@ pub const StackAllocator = struct {
return compare - bufferStart; 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));
}
/// Attempt to allocate exactly `len` bytes aligned to `1 << ptr_align`. /// Attempt to allocate exactly `len` bytes aligned to `1 << ptr_align`.
/// ///
/// `ret_addr` is optionally provided as the first return address of the /// `ret_addr` is optionally provided as the first return address of the
@ -430,11 +437,12 @@ pub const StackAllocator = struct {
/// has been provided. /// has been provided.
fn alloc(ctx: *anyopaque, len: usize, ptr_align: u8, ret_addr: usize) ?[*]u8 { fn alloc(ctx: *anyopaque, len: usize, ptr_align: u8, ret_addr: usize) ?[*]u8 {
const self: *StackAllocator = @ptrCast(@alignCast(ctx)); const self: *StackAllocator = @ptrCast(@alignCast(ctx));
if(len >= self.buffer.len) return self.backingAllocator.rawAlloc(len, ptr_align, ret_addr);
const start = std.mem.alignForward(usize, self.index, @as(usize, 1) << @intCast(ptr_align)); const start = std.mem.alignForward(usize, self.index, @as(usize, 1) << @intCast(ptr_align));
if(start + len >= self.buffer.len) return self.backingAllocator.rawAlloc(len, ptr_align, ret_addr); const end = getTrueAllocationEnd(start, len);
self.allocationList.append(.{.start = @intCast(start), .len = @intCast(len)}); if(end >= self.buffer.len) return self.backingAllocator.rawAlloc(len, ptr_align, ret_addr);
self.index = start + len; const trailer = self.getTrailerBefore(end);
trailer.* = .{.wasFreed = false, .previousAllocationTrailer = @intCast(self.index)};
self.index = end;
return self.buffer.ptr + start; return self.buffer.ptr + start;
} }
@ -456,16 +464,18 @@ pub const StackAllocator = struct {
fn resize(ctx: *anyopaque, buf: []u8, buf_align: u8, new_len: usize, ret_addr: usize) bool { fn resize(ctx: *anyopaque, buf: []u8, buf_align: u8, new_len: usize, ret_addr: usize) bool {
const self: *StackAllocator = @ptrCast(@alignCast(ctx)); const self: *StackAllocator = @ptrCast(@alignCast(ctx));
if(self.isInsideBuffer(buf)) { if(self.isInsideBuffer(buf)) {
const top = &self.allocationList.items[self.allocationList.items.len - 1]; const start = self.indexInBuffer(buf);
std.debug.assert(top.start == self.indexInBuffer(buf)); // Can only resize the top element. const end = getTrueAllocationEnd(start, buf.len);
std.debug.assert(top.len == buf.len); if(end != self.index) return false;
std.debug.assert(self.index >= top.start + top.len); const newEnd = getTrueAllocationEnd(start, new_len);
if(top.start + new_len >= self.buffer.len) { if(newEnd >= self.buffer.len) return false;
return false;
} const trailer = self.getTrailerBefore(end);
self.index -= top.len; std.debug.assert(!trailer.wasFreed);
self.index += new_len; const newTrailer = self.getTrailerBefore(newEnd);
top.len = @intCast(new_len);
newTrailer.* = .{.wasFreed = false, .previousAllocationTrailer = trailer.previousAllocationTrailer};
self.index = newEnd;
return true; return true;
} else { } else {
return self.backingAllocator.rawResize(buf, buf_align, new_len, ret_addr); return self.backingAllocator.rawResize(buf, buf_align, new_len, ret_addr);
@ -486,11 +496,24 @@ pub const StackAllocator = struct {
fn free(ctx: *anyopaque, buf: []u8, buf_align: u8, ret_addr: usize) void { fn free(ctx: *anyopaque, buf: []u8, buf_align: u8, ret_addr: usize) void {
const self: *StackAllocator = @ptrCast(@alignCast(ctx)); const self: *StackAllocator = @ptrCast(@alignCast(ctx));
if(self.isInsideBuffer(buf)) { if(self.isInsideBuffer(buf)) {
const top = self.allocationList.pop(); const start = self.indexInBuffer(buf);
std.debug.assert(top.start == self.indexInBuffer(buf)); // Can only free the top element. const end = getTrueAllocationEnd(start, buf.len);
std.debug.assert(top.len == buf.len); const trailer = self.getTrailerBefore(end);
std.debug.assert(self.index >= top.start + top.len); std.debug.assert(!trailer.wasFreed); // Double Free
self.index = top.start;
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 { } else {
self.backingAllocator.rawFree(buf, buf_align, ret_addr); self.backingAllocator.rawFree(buf, buf_align, ret_addr);
} }