mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-07-14 15:56:13 +03:00
116 lines
3.6 KiB
Zig
116 lines
3.6 KiB
Zig
//! This file contains helpers for wasm compilation.
|
|
const std = @import("std");
|
|
const builtin = @import("builtin");
|
|
const options = @import("build_options");
|
|
|
|
comptime {
|
|
if (!builtin.target.isWasm()) {
|
|
@compileError("wasm.zig should only be analyzed for wasm32 builds");
|
|
}
|
|
}
|
|
|
|
/// True if we're in shared memory mode. If true, then the memory buffer
|
|
/// in JS will be backed by a SharedArrayBuffer and some behaviors change.
|
|
pub const shared_mem = options.wasm_shared;
|
|
|
|
/// The allocator to use in wasm environments.
|
|
///
|
|
/// The return values of this should NOT be sent to the host environment
|
|
/// unless toHostOwned is called on them. In this case, the caller is expected
|
|
/// to call free. If a pointer is NOT host-owned, then the wasm module is
|
|
/// expected to call the normal alloc.free/destroy functions.
|
|
pub const alloc = if (builtin.is_test)
|
|
std.testing.allocator
|
|
else
|
|
std.heap.wasm_allocator;
|
|
|
|
/// For host-owned allocations:
|
|
/// We need to keep track of our own pointer lengths because Zig
|
|
/// allocators usually don't do this and we need to be able to send
|
|
/// a direct pointer back to the host system. A more appropriate thing
|
|
/// to do would be to probably make a custom allocator that keeps track
|
|
/// of size.
|
|
var allocs: std.AutoHashMapUnmanaged([*]u8, usize) = .{};
|
|
|
|
/// Allocate len bytes and return a pointer to the memory in the host.
|
|
/// The data is not zeroed.
|
|
pub export fn malloc(len: usize) ?[*]u8 {
|
|
return alloc_(len) catch return null;
|
|
}
|
|
|
|
fn alloc_(len: usize) ![*]u8 {
|
|
// Create the allocation
|
|
const slice = try alloc.alloc(u8, len);
|
|
errdefer alloc.free(slice);
|
|
|
|
// Store the size so we can deallocate later
|
|
try allocs.putNoClobber(alloc, slice.ptr, slice.len);
|
|
errdefer _ = allocs.remove(slice.ptr);
|
|
|
|
return slice.ptr;
|
|
}
|
|
|
|
/// Free an allocation from malloc.
|
|
pub export fn free(ptr: ?[*]u8) void {
|
|
if (ptr) |v| {
|
|
if (allocs.get(v)) |len| {
|
|
const slice = v[0..len];
|
|
alloc.free(slice);
|
|
_ = allocs.remove(v);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Convert an allocated pointer of any type to a host-owned pointer.
|
|
/// This pushes the responsibility to free it to the host. The returned
|
|
/// pointer will match the pointer but is typed correctly for returning
|
|
/// to the host.
|
|
pub fn toHostOwned(ptr: anytype) ![*]u8 {
|
|
// Convert our pointer to a byte array
|
|
const info = @typeInfo(@TypeOf(ptr)).Pointer;
|
|
const T = info.child;
|
|
const size = @sizeOf(T);
|
|
const casted = @as([*]u8, @ptrFromInt(@intFromPtr(ptr)));
|
|
|
|
// Store the information about it
|
|
try allocs.putNoClobber(alloc, casted, size);
|
|
errdefer _ = allocs.remove(casted);
|
|
|
|
return casted;
|
|
}
|
|
|
|
/// Returns true if the value is host owned.
|
|
pub fn isHostOwned(ptr: anytype) bool {
|
|
const casted = @as([*]u8, @ptrFromInt(@intFromPtr(ptr)));
|
|
return allocs.contains(casted);
|
|
}
|
|
|
|
/// Convert a pointer back to a module-owned value. The caller is expected
|
|
/// to cast or have the valid pointer for alloc calls.
|
|
pub fn toModuleOwned(ptr: anytype) void {
|
|
const casted = @as([*]u8, @ptrFromInt(@intFromPtr(ptr)));
|
|
_ = allocs.remove(casted);
|
|
}
|
|
|
|
test "basics" {
|
|
const testing = std.testing;
|
|
const buf = malloc(32).?;
|
|
try testing.expect(allocs.size == 1);
|
|
free(buf);
|
|
try testing.expect(allocs.size == 0);
|
|
}
|
|
|
|
test "toHostOwned" {
|
|
const testing = std.testing;
|
|
|
|
const Point = struct { x: u32 = 0, y: u32 = 0 };
|
|
const p = try alloc.create(Point);
|
|
errdefer alloc.destroy(p);
|
|
const ptr = try toHostOwned(p);
|
|
try testing.expect(allocs.size == 1);
|
|
try testing.expect(isHostOwned(p));
|
|
try testing.expect(isHostOwned(ptr));
|
|
free(ptr);
|
|
try testing.expect(allocs.size == 0);
|
|
}
|