diff options
-rw-r--r-- | build.zig | 2 | ||||
-rw-r--r-- | src/alloc.zig | 175 | ||||
-rw-r--r-- | src/api/demo/lib/api.ts | 6 | ||||
-rw-r--r-- | src/main_wasm.zig | 138 |
4 files changed, 259 insertions, 62 deletions
@@ -36,7 +36,7 @@ pub fn build(b: *std.build.Builder) void { // exe.is_linking_libc = false; // exe.is_dynamic = true; var lib = b.addExecutable("esdev", "src/main_wasm.zig"); - + lib.single_threaded = true; // exe.want_lto = true; // exe.linkLibrary(lib); diff --git a/src/alloc.zig b/src/alloc.zig index ed790d096..8d3e3520b 100644 --- a/src/alloc.zig +++ b/src/alloc.zig @@ -1,4 +1,5 @@ const std = @import("std"); +const builtin = @import("builtin"); const STATIC_MEMORY_SIZE = 256000; pub var static_manager: ?std.heap.ArenaAllocator = null; @@ -19,3 +20,177 @@ test "GlobalAllocator" { var testType = try static.alloc(u8, 10); testType[1] = 1; } + +pub const HunkSide = struct { + pub const VTable = struct { + alloc: fn (self: *Hunk, n: usize, alignment: u29) std.mem.Allocator.Error![]u8, + getMark: fn (self: *Hunk) usize, + freeToMark: fn (self: *Hunk, pos: usize) void, + }; + + hunk: *Hunk, + vtable: *const VTable, + allocator: std.mem.Allocator, + + pub fn init(hunk: *Hunk, vtable: *const VTable) HunkSide { + return .{ + .hunk = hunk, + .vtable = vtable, + .allocator = .{ + .allocFn = allocFn, + .resizeFn = resizeFn, + }, + }; + } + + pub fn getMark(self: HunkSide) usize { + return self.vtable.getMark(self.hunk); + } + + pub fn freeToMark(self: HunkSide, pos: usize) void { + self.vtable.freeToMark(self.hunk, pos); + } + + fn allocFn(allocator: *std.mem.Allocator, len: usize, ptr_align: u29, len_align: u29, ret_addr: usize) std.mem.Allocator.Error![]u8 { + const self = @fieldParentPtr(HunkSide, "allocator", allocator); + + return try self.vtable.alloc(self.hunk, len, ptr_align); + } + + fn resizeFn(allocator: *std.mem.Allocator, old_mem: []u8, old_align: u29, new_size: usize, len_align: u29, ret_addr: usize) std.mem.Allocator.Error!usize { + if (new_size > old_mem.len) { + return error.OutOfMemory; + } + if (new_size == 0) { + return 0; + } + return std.mem.alignAllocLen(old_mem.len, new_size, len_align); + } +}; + +pub const Hunk = struct { + low_used: usize, + high_used: usize, + buffer: []u8, + + pub fn init(buffer: []u8) Hunk { + return .{ + .low_used = 0, + .high_used = 0, + .buffer = buffer, + }; + } + + pub fn low(self: *Hunk) HunkSide { + const GlobalStorage = struct { + const vtable: HunkSide.VTable = .{ + .alloc = allocLow, + .getMark = getLowMark, + .freeToMark = freeToLowMark, + }; + }; + return HunkSide.init(self, &GlobalStorage.vtable); + } + + pub fn high(self: *Hunk) HunkSide { + const GlobalStorage = struct { + const vtable: HunkSide.VTable = .{ + .alloc = allocHigh, + .getMark = getHighMark, + .freeToMark = freeToHighMark, + }; + }; + return HunkSide.init(self, &GlobalStorage.vtable); + } + + pub fn allocLow(self: *Hunk, n: usize, alignment: u29) ![]u8 { + const start = @ptrToInt(self.buffer.ptr); + const adjusted_index = std.mem.alignForward(start + self.low_used, alignment) - start; + const new_low_used = adjusted_index + n; + if (new_low_used > self.buffer.len - self.high_used) { + return error.OutOfMemory; + } + const result = self.buffer[adjusted_index..new_low_used]; + self.low_used = new_low_used; + return result; + } + + pub fn allocHigh(self: *Hunk, n: usize, alignment: u29) ![]u8 { + const addr = @ptrToInt(self.buffer.ptr) + self.buffer.len - self.high_used; + const rem = @rem(addr, alignment); + const march_backward_bytes = rem; + const adjusted_index = self.high_used + march_backward_bytes; + const new_high_used = adjusted_index + n; + if (new_high_used > self.buffer.len - self.low_used) { + return error.OutOfMemory; + } + const start = self.buffer.len - adjusted_index - n; + const result = self.buffer[start .. start + n]; + self.high_used = new_high_used; + return result; + } + + pub fn getLowMark(self: *Hunk) usize { + return self.low_used; + } + + pub fn getHighMark(self: *Hunk) usize { + return self.high_used; + } + + pub fn freeToLowMark(self: *Hunk, pos: usize) void { + std.debug.assert(pos <= self.low_used); + if (pos < self.low_used) { + if (std.builtin.mode == std.builtin.Mode.Debug) { + std.mem.set(u8, self.buffer[pos..self.low_used], 0xcc); + } + self.low_used = pos; + } + } + + pub fn freeToHighMark(self: *Hunk, pos: usize) void { + std.debug.assert(pos <= self.high_used); + if (pos < self.high_used) { + if (std.builtin.mode == std.builtin.Mode.Debug) { + const i = self.buffer.len - self.high_used; + const n = self.high_used - pos; + std.mem.set(u8, self.buffer[i .. i + n], 0xcc); + } + self.high_used = pos; + } + } +}; + +test "Hunk" { + // test a few random operations. very low coverage. write more later + var buf: [100]u8 = undefined; + var hunk = Hunk.init(buf[0..]); + + const high_mark = hunk.getHighMark(); + + _ = try hunk.low().allocator.alloc(u8, 7); + _ = try hunk.high().allocator.alloc(u8, 8); + + std.testing.expectEqual(@as(usize, 7), hunk.low_used); + std.testing.expectEqual(@as(usize, 8), hunk.high_used); + + _ = try hunk.high().allocator.alloc(u8, 8); + + std.testing.expectEqual(@as(usize, 16), hunk.high_used); + + const low_mark = hunk.getLowMark(); + + _ = try hunk.low().allocator.alloc(u8, 100 - 7 - 16); + + std.testing.expectEqual(@as(usize, 100 - 16), hunk.low_used); + + std.testing.expectError(error.OutOfMemory, hunk.high().allocator.alloc(u8, 1)); + + hunk.freeToLowMark(low_mark); + + _ = try hunk.high().allocator.alloc(u8, 1); + + hunk.freeToHighMark(high_mark); + + std.testing.expectEqual(@as(usize, 0), hunk.high_used); +} diff --git a/src/api/demo/lib/api.ts b/src/api/demo/lib/api.ts index e8a626b10..860dfa2ac 100644 --- a/src/api/demo/lib/api.ts +++ b/src/api/demo/lib/api.ts @@ -126,7 +126,7 @@ export class ESDev { } ESDev[wasm_imports_sym].memory = new WebAssembly.Memory({ - initial: 18, + initial: 20, // shared: typeof SharedArrayBuffer !== "undefined", maximum: typeof SharedArrayBuffer !== "undefined" ? 5000 : undefined, }); @@ -170,13 +170,13 @@ export class ESDev { if (bb._data.buffer !== scratch.buffer) { scratch = bb._data; } - + ESDev.wasm_exports.cycleStart(); const ptr = ESDev.wasm_exports.malloc(data.byteLength); this._wasmPtrToSlice(ptr).set(data); const resp_ptr = ESDev.wasm_exports.transform(ptr); var _bb = new ByteBuffer(this._wasmPtrToSlice(resp_ptr)); const response = Schema.decodeTransformResponse(_bb); - ESDev.wasm_exports.cycle(ptr, resp_ptr); + ESDev.wasm_exports.cycleEnd(); return response; } } diff --git a/src/main_wasm.zig b/src/main_wasm.zig index 250f5c444..a3b31e995 100644 --- a/src/main_wasm.zig +++ b/src/main_wasm.zig @@ -16,7 +16,7 @@ const fs = @import("fs.zig"); const Schema = @import("api/schema.zig").Api; const builtin = std.builtin; const MainPanicHandler = panicky.NewPanicHandler(panicky.default_panic); -const zee = @import("zee_alloc.zig"); +// const zee = @import("zee_alloc.zig"); pub fn panic(msg: []const u8, error_return_trace: ?*std.builtin.StackTrace) noreturn { if (MainPanicHandler.Singleton) |singleton| { @@ -146,17 +146,36 @@ pub extern fn console_error(abi: Uint8Array.Abi) void; pub extern fn console_warn(abi: Uint8Array.Abi) void; pub extern fn console_info(abi: Uint8Array.Abi) void; -const ZeeAlloc = zee.ZeeAlloc(.{}); -var zee_instance: ZeeAlloc = undefined; +// const ZeeAlloc = zee.ZeeAlloc(.{}); +// var zee_instance: ZeeAlloc = undefined; +// const Gpa = std.heap.GeneralPurposeAllocator(.{}); +// var arena: std.heap.ArenaAllocator = undefined; +// var gpa: Gpa = undefined; +var hunk: alloc.Hunk = undefined; +var hunk_high: alloc.HunkSide = undefined; +var hunk_low: alloc.HunkSide = undefined; +var perma_hunk: alloc.Hunk = undefined; +var perma_hunk_high_alloc: *std.mem.Allocator = undefined; +var perma_hunk_high: alloc.HunkSide = undefined; +var perma_hunk_low_alloc: *std.mem.Allocator = undefined; +var perma_hunk_low: alloc.HunkSide = undefined; +var last_start_high: usize = 0; +var last_start_low: usize = 0; pub const Exports = struct { fn init() callconv(.C) i32 { - // const Gpa = std.heap.GeneralPurposeAllocator(.{}); + var perma_hunk_buf = std.heap.page_allocator.alloc(u8, 128000) catch return -1; + perma_hunk = alloc.Hunk.init(perma_hunk_buf); + perma_hunk_high = perma_hunk.high(); + perma_hunk_low = perma_hunk.low(); + + perma_hunk_high_alloc = &perma_hunk_low.allocator; + // var gpa = Gpa{}; // var allocator = &gpa.allocator; // alloc.setup(allocator) catch return -1; - var out_buffer = std.heap.page_allocator.alloc(u8, 2048) catch return -1; - var err_buffer = std.heap.page_allocator.alloc(u8, 2048) catch return -1; - var output = std.heap.page_allocator.create(Output.Source) catch return -1; + var out_buffer = perma_hunk_low.allocator.alloc(u8, 4096) catch return -1; + var err_buffer = perma_hunk_low.allocator.alloc(u8, 4096) catch return -1; + var output = perma_hunk_low.allocator.create(Output.Source) catch return -1; var stream = std.io.fixedBufferStream(out_buffer); var err_stream = std.io.fixedBufferStream(err_buffer); output.* = Output.Source.init( @@ -183,11 +202,15 @@ pub const Exports = struct { ) catch return -1; if (alloc.needs_setup) { - alloc.setup(ZeeAlloc.wasm_allocator) catch return -1; + var buf = std.heap.page_allocator.alloc(u8, 26843545) catch return -1; + hunk = alloc.Hunk.init(buf); + hunk_high = hunk.high(); + hunk_low = hunk.low(); + alloc.dynamic = &hunk_high.allocator; + alloc.static = &hunk_low.allocator; + alloc.needs_setup = false; } - _ = @wasmMemoryGrow(0, 600); - Output.printErrorable("Initialized.", .{}) catch |err| { var name = alloc.static.alloc(u8, @errorName(err).len) catch unreachable; std.mem.copy(u8, name, @errorName(err)); @@ -203,63 +226,61 @@ pub const Exports = struct { // Output.print("Req {s}", .{req}); // alloc.dynamic.free(Uint8Array.toSlice(abi)); const resp = api.?.transform(req) catch return Uint8Array.empty(); - alloc.dynamic.free(req.contents); - - if (req.path) |path| alloc.dynamic.free(path); var res = Uint8Array.encode(Schema.TransformResponse, resp) catch return Uint8Array.empty(); - // this is stupid. - for (resp.files) |file| { - alloc.dynamic.free(file.data); - alloc.dynamic.free(file.path); - } return res; } // Reset - fn cycle(req: Uint8Array.Abi, res: Uint8Array.Abi) callconv(.C) void { - alloc.dynamic.free(Uint8Array.toSlice(res)); - alloc.dynamic.free(Uint8Array.toSlice(req)); + fn cycleStart() callconv(.C) void { + last_start_high = hunk.getHighMark(); + last_start_low = hunk.getLowMark(); } - fn malloc(size: usize) callconv(.C) ?*c_void { - if (size == 0) { - return null; + fn cycleEnd() callconv(.C) void { + if (last_start_high > 0) { + hunk.freeToHighMark(last_start_high); + last_start_high = 0; } - //const result = alloc.dynamic.alloc(u8, size) catch return null; - const result = alloc.dynamic.allocFn(alloc.dynamic, size, 1, 1, 0) catch return null; - return result.ptr; - } - fn calloc(num_elements: usize, element_size: usize) callconv(.C) ?*c_void { - const size = num_elements *% element_size; - const c_ptr = @call(.{ .modifier = .never_inline }, malloc, .{size}); - if (c_ptr) |ptr| { - const p = @ptrCast([*]u8, ptr); - @memset(p, 0, size); + + if (last_start_low > 0) { + hunk.freeToLowMark(last_start_low); + last_start_low = 0; } - return c_ptr; } - fn realloc(c_ptr: ?*c_void, new_size: usize) callconv(.C) ?*c_void { - if (new_size == 0) { - @call(.{ .modifier = .never_inline }, free, .{c_ptr}); - return null; - } else if (c_ptr) |ptr| { - // Use a synthetic slice - const p = @ptrCast([*]u8, ptr); - const result = alloc.dynamic.realloc(p[0..1], new_size) catch return null; - return @ptrCast(*c_void, result.ptr); - } else { - return @call(.{ .modifier = .never_inline }, malloc, .{new_size}); + + fn malloc(size: usize) callconv(.C) Uint8Array.Abi { + if (size == 0) { + return 0; } + const result = alloc.dynamic.alloc(u8, size) catch unreachable; + return Uint8Array.fromSlice(result); } - fn free(c_ptr: ?*c_void) callconv(.C) void { - if (c_ptr) |ptr| { - // Use a synthetic slice. zee_alloc will free via corresponding metadata. - const p = @ptrCast([*]u8, ptr); - //alloc.dynamic.free(p[0..1]); - _ = alloc.dynamic.resizeFn(alloc.dynamic, p[0..1], 0, 0, 0, 0) catch unreachable; - } + // fn calloc(num_elements: usize, element_size: usize) callconv(.C) ?*c_void { + // const size = num_elements *% element_size; + // const c_ptr = @call(.{ .modifier = .never_inline }, malloc, .{size}); + // if (c_ptr) |ptr| { + // const p = @ptrCast([*]u8, ptr); + // @memset(p, 0, size); + // } + // return c_ptr; + // } + // fn realloc(c_ptr: ?*c_void, new_size: usize) callconv(.C) ?*c_void { + // if (new_size == 0) { + // // @call(.{ .modifier = .never_inline }, free, .{@intCast(Uint8Array.Abi, c_ptr.?)}); + // return null; + // } else if (c_ptr) |ptr| { + // // Use a synthetic slice + // const p = @ptrCast([*]u8, ptr); + // const result = alloc.dynamic.realloc(p[0..1], new_size) catch return null; + // return @ptrCast(*c_void, result.ptr); + // } else { + // return @call(.{ .modifier = .never_inline }, malloc, .{new_size}); + // } + // } + fn free(abi: Uint8Array.Abi) callconv(.C) void { + alloc.dynamic.free(Uint8Array.toSlice(abi)); } }; @@ -269,9 +290,10 @@ comptime { @export(Exports.init, .{ .name = "init", .linkage = .Strong }); @export(Exports.transform, .{ .name = "transform", .linkage = .Strong }); @export(Exports.malloc, .{ .name = "malloc", .linkage = .Strong }); - @export(Exports.calloc, .{ .name = "calloc", .linkage = .Strong }); - @export(Exports.realloc, .{ .name = "realloc", .linkage = .Strong }); - @export(Exports.cycle, .{ .name = "cycle", .linkage = .Strong }); + // @export(Exports.calloc, .{ .name = "calloc", .linkage = .Strong }); + // @export(Exports.realloc, .{ .name = "realloc", .linkage = .Strong }); + @export(Exports.cycleStart, .{ .name = "cycleStart", .linkage = .Strong }); + @export(Exports.cycleEnd, .{ .name = "cycleEnd", .linkage = .Strong }); @export(Exports.free, .{ .name = "free", .linkage = .Strong }); } @@ -279,7 +301,7 @@ pub fn main() anyerror!void { std.mem.doNotOptimizeAway(Exports.init); std.mem.doNotOptimizeAway(Exports.transform); std.mem.doNotOptimizeAway(Exports.malloc); - std.mem.doNotOptimizeAway(Exports.calloc); - std.mem.doNotOptimizeAway(Exports.realloc); + // std.mem.doNotOptimizeAway(Exports.calloc); + // std.mem.doNotOptimizeAway(Exports.realloc); std.mem.doNotOptimizeAway(Exports.free); } |