aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--build.zig2
-rw-r--r--src/alloc.zig175
-rw-r--r--src/api/demo/lib/api.ts6
-rw-r--r--src/main_wasm.zig138
4 files changed, 259 insertions, 62 deletions
diff --git a/build.zig b/build.zig
index 5981d7b70..bb1e9ecac 100644
--- a/build.zig
+++ b/build.zig
@@ -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);
}