Compare commits

...

17 Commits

43 changed files with 13616 additions and 861 deletions

1
.gitattributes vendored
View File

@@ -1,2 +1,3 @@
*.dll filter=lfs diff=lfs merge=lfs -text *.dll filter=lfs diff=lfs merge=lfs -text
*.pdf filter=lfs diff=lfs merge=lfs -text *.pdf filter=lfs diff=lfs merge=lfs -text
*.so filter=lfs diff=lfs merge=lfs -text

View File

@@ -1,5 +0,0 @@
{
"files.exclude":{
"**/.zig-cache": true,
},
}

44
castle.code-workspace Normal file
View File

@@ -0,0 +1,44 @@
{
"folders": [
{
"name": "cjit",
"path": "packages/cjit"
},
{
"name": "js",
"path": "packages/js"
},
{
"name": "media",
"path": "packages/media"
},
{
"name": "myid",
"path": "packages/myid"
},
{
"name": "sciter",
"path": "packages/sciter"
},
{
"name": "tcc",
"path": "packages/tcc"
},
{
"name": "vecmath",
"path": "packages/vecmath"
},
{
"name": "x11",
"path": "packages/x11"
}
],
"settings": {
"files.exclude": {
"**/.zig-cache": true,
},
"files.associations": {
"**/packages/tcc/vendor/*.{def,h}": "c",
},
},
}

44
packages/cjit/build.zig Normal file
View File

@@ -0,0 +1,44 @@
const std = @import("std");
pub fn build(b: *std.Build) void {
const target = b.standardTargetOptions(.{
.whitelist = &.{
.{
.cpu_arch = .x86_64,
.os_tag = .windows,
.abi = .gnu,
},
.{
.cpu_arch = .x86_64,
.os_tag = .linux,
.abi = .gnu,
},
},
});
const optimize = b.standardOptimizeOption(.{});
const module = b.addModule("cjit", .{
.target = target,
.optimize = optimize,
.root_source_file = b.path("src/root.zig"),
});
const module_internal_test = b.addTest(.{ .root_module = module });
const run_internal_test = b.addRunArtifact(module_internal_test);
const module_external_test = b.addTest(.{
.root_module = b.createModule(.{
.target = b.resolveTargetQuery(.{}),
.optimize = .Debug,
.root_source_file = b.path("test/root.zig"),
.imports = &.{
.{ .name = "cjit", .module = module },
},
}),
});
const run_external_test = b.addRunArtifact(module_external_test);
const step_test = b.step("test", "Run tests");
step_test.dependOn(&run_internal_test.step);
step_test.dependOn(&run_external_test.step);
}

View File

@@ -0,0 +1,11 @@
.{
.name = .cjit,
.version = "0.0.0",
.minimum_zig_version = "0.15.2",
.paths = .{
"src",
"build.zig",
"build.zig.zon",
},
.fingerprint = 0xaec0accc19243440,
}

View File

@@ -0,0 +1,154 @@
const std = @import("std");
const builtin = @import("builtin");
const Self = @This();
const tokens = @import("tokens.zig");
const types = @import("types.zig");
const Sections = @import("Sections.zig");
const StackValue = @import("StackValue.zig");
const Tokenizer = tokens.Tokenizer;
const Type = types.Type;
pub const virtual_stack_size = 256;
allocator: std.mem.Allocator,
arena: std.heap.ArenaAllocator,
symbols_needed: std.StringHashMapUnmanaged(ExternSymbol) = .{},
symbols_provided: std.StringHashMapUnmanaged(InternSymbol) = .{},
symbols_located: std.StringHashMapUnmanaged(LocatedSymbol) = .{},
relocation_table: std.ArrayList(Relocation) = .{},
/// Bounded, preallocated with the capacity of `virtual_stack_size`.
virtual_stack: std.ArrayList(StackValue),
tokenizer: Tokenizer,
sections: Sections = .{},
pub const ExternSymbol = struct {
name: []const u8,
c_type: Type,
ptr: ?*anyopaque,
};
pub const InternSymbol = struct {
name: []const u8,
public: bool,
c_type: Type,
location: Location,
};
pub const Location = union(enum) {
text: usize,
data: usize,
rodata: usize,
};
pub const LocatedSymbol = struct {
name: []const u8,
public: bool,
c_type: Type,
ptr: ?*anyopaque,
};
pub const Relocation = struct {
addr: usize,
location: Location,
type: RelocationType,
};
pub const RelocationType = enum {
global_offset_table,
rip_disp32,
};
pub fn init(allocator: std.mem.Allocator) !Self {
var arena: std.heap.ArenaAllocator = .init(allocator);
const arena_allocator = arena.allocator();
errdefer arena.deinit();
const virtual_stack_buffer = try arena_allocator.alloc(StackValue, virtual_stack_size);
const tokenizer = try Tokenizer.init(arena_allocator);
return .{
.allocator = allocator,
.arena = arena,
.virtual_stack = .initBuffer(virtual_stack_buffer),
.tokenizer = tokenizer,
};
}
pub fn deinit(self: *Self) void {
self.symbols_needed.deinit(self.allocator);
self.symbols_provided.deinit(self.allocator);
self.symbols_located.deinit(self.allocator);
self.relocation_table.deinit(self.allocator);
self.arena.deinit();
// TODO deinit sections (stage-depentent)
self.* = undefined;
}
pub fn compile(self: *Self, filename: []const u8, code: []const u8) !void {
self.tokenizer.setSource(filename, code);
while (try self.tokenizer.nextToken(self.arena.allocator())) |token| {
_ = token;
std.debug.panic("Not implemented", .{});
}
}
pub fn setSymbol(self: *Self, comptime T: type, name: []const u8, ptr: *const T) !void {
const symbol = self.symbols_needed.getPtr(name) orelse return error.SymbolNotFound;
if (!types.isCompatible(T, symbol.c_type)) return error.IncompatibleType;
// TODO Figure out const-correctness
symbol.ptr = @ptrCast(@constCast(ptr));
}
pub fn link(self: *Self) !void {
try self.sections.relocateSections(self.allocator);
const text_base = @intFromPtr(self.sections.text.data.items.ptr);
const data_base = @intFromPtr(self.sections.data.data.items.ptr);
const rodata_base = @intFromPtr(self.sections.rodata.data.items.ptr);
for (self.relocation_table.items) |relocation| {
const target_addr = switch (relocation.location) {
.text => |offset| text_base + offset,
.data => |offset| data_base + offset,
.rodata => |offset| rodata_base + offset,
};
switch (relocation.type) {
.global_offset_table => {
std.mem.writeInt(
usize,
self.sections.rodata.data.items[relocation.addr..][0..@sizeOf(usize)],
target_addr,
builtin.cpu.arch.endian(),
);
},
.rip_disp32 => {
const rip = text_base + relocation.addr + 4;
const disp64: isize = @bitCast(rip -% target_addr);
const disp32 = std.math.cast(i32, disp64) orelse return error.RelocationError;
std.mem.writeInt(
i32,
self.sections.text.data.items[relocation.addr..][0..4],
disp32,
builtin.cpu.arch.endian(),
);
},
}
}
self.relocation_table.clearAndFree(self.allocator);
try self.sections.protectSections();
}
pub fn getSymbol(self: *const Self, comptime T: type, name: []const u8) ?*const T {
const symbol = self.symbols_located.get(name) orelse return null;
return @ptrCast(@alignCast(symbol.ptr));
}
fn create(self: *Self, comptime T: type) error{OutOfMemory}!*T {
return self.arena.allocator().create(T);
}

View File

@@ -0,0 +1,185 @@
const std = @import("std");
const builtin = @import("builtin");
const Self = @This();
text: Section = .{ .protection = .executable },
data: Section = .{ .protection = .read_write },
rodata: Section = .{ .protection = .read_only },
pub const Section = struct {
data: std.ArrayList(u8) = .{},
protection: Protection,
pub const Protection = enum {
executable,
read_only,
read_write,
};
pub fn writeValue(self: *Section, value: anytype, allocator: std.mem.Allocator) !void {
const T = @TypeOf(value);
std.debug.assert(std.meta.hasUniqueRepresentation(T));
const bytes = std.mem.asBytes(&value);
try self.writeBytes(bytes, allocator);
}
pub fn writeByte(self: *Section, byte: u8, allocator: std.mem.Allocator) !void {
try self.data.append(allocator, byte);
}
pub fn writeBytes(self: *Section, data: []const u8, allocator: std.mem.Allocator) !void {
try self.data.appendSlice(allocator, data);
}
pub fn alignForward(self: *Section, alignment: u16, allocator: std.mem.Allocator) !void {
const ptr = self.data.items.len;
const ptr_aligned = std.mem.alignForward(usize, ptr, alignment);
const padding = ptr_aligned - ptr;
try self.data.appendNTimes(allocator, 0, padding);
}
pub fn pageCount(self: Section) usize {
const page_size = std.heap.pageSize();
const section_size = self.data.items.len;
return @divFloor(section_size + page_size - 1, page_size);
}
};
pub fn relocateSections(self: *Self, allocator: std.mem.Allocator) !void {
const page_size = std.heap.pageSize();
const text_pages = self.text.pageCount();
const text_bytes = text_pages * page_size;
const data_pages = self.data.pageCount();
const data_bytes = data_pages * page_size;
const rodata_pages = self.rodata.pageCount();
const rodata_bytes = rodata_pages * page_size;
const total_pages = text_pages + data_pages + rodata_pages;
const total_bytes = text_bytes + data_bytes + rodata_bytes;
std.debug.assert(total_bytes == total_pages * page_size);
const ptr: [*]u8 = sw: switch (builtin.os.tag) {
.windows => {
const windows = std.os.windows;
const ntdll = windows.ntdll;
var base_addr: ?*anyopaque = null;
var size: windows.SIZE_T = total_bytes;
const status = ntdll.NtAllocateVirtualMemory(
windows.GetCurrentProcess(),
@ptrCast(&base_addr),
0,
&size,
windows.MEM_COMMIT | windows.MEM_RESERVE,
windows.PAGE_READWRITE,
);
if (status == .SUCCESS) {
break :sw @ptrCast(base_addr);
} else {
return error.OutOfMemory;
}
},
.linux => {
const linux = std.os.linux;
const rc = linux.mmap(
null,
total_bytes,
linux.PROT.READ | linux.PROT.WRITE,
.{ .TYPE = .PRIVATE, .ANONYMOUS = true },
-1,
0,
);
const status: linux.E = .init(rc);
if (status == .SUCCESS) {
break :sw @ptrFromInt(rc);
} else {
return error.OutOfMemory;
}
},
else => @compileError("Operating system " ++ @tagName(builtin.os.tag) ++ " not supported"),
};
const text_slice = ptr[0..text_bytes];
const data_slice = ptr[text_bytes .. text_bytes + data_bytes];
const rodata_slice = ptr[text_bytes + data_bytes .. text_bytes + data_bytes + rodata_bytes];
@memcpy(text_slice[0..self.text.data.items.len], self.text.data.items);
@memcpy(data_slice[0..self.data.data.items.len], self.data.data.items);
@memcpy(rodata_slice[0..self.rodata.data.items.len], self.rodata.data.items);
self.text.data.clearAndFree(allocator);
self.data.data.clearAndFree(allocator);
self.rodata.data.clearAndFree(allocator);
self.text.data = .{ .items = text_slice, .capacity = text_bytes };
self.data.data = .{ .items = data_slice, .capacity = data_bytes };
self.rodata.data = .{ .items = rodata_slice, .capacity = rodata_bytes };
}
pub fn protectSections(self: *Self) !void {
const sections = [_]*Section{ &self.text, &self.data, &self.rodata };
for (sections) |section| {
switch (builtin.os.tag) {
.windows => {
const windows = std.os.windows;
const ntdll = windows.ntdll;
const protection = switch (section.protection) {
.executable => windows.PAGE_EXECUTE,
.read_only => windows.PAGE_READONLY,
.read_write => windows.PAGE_READWRITE,
};
var base_addr: ?*anyopaque = section.data.items.ptr;
var size: windows.SIZE_T = section.data.capacity;
var old_protection: u32 = undefined;
const status = ntdll.NtProtectVirtualMemory(
windows.GetCurrentProcess(),
&base_addr,
&size,
protection,
&old_protection,
);
if (status != .SUCCESS) {
return error.ProtectionError;
}
},
.linux => {
const linux = std.os.linux;
const protection: usize = switch (section.protection) {
.executable => linux.PROT.EXEC,
.read_only => linux.PROT.READ,
.read_write => linux.PROT.READ | linux.PROT.WRITE,
};
const rc = linux.mprotect(
section.data.items.ptr,
section.data.capacity,
protection,
);
const status: linux.E = .init(rc);
if (status != .SUCCESS) {
return error.ProtectionError;
}
},
else => @compileError("Operating system " ++ @tagName(builtin.os.tag) ++ " not supported"),
}
}
}
pub fn freeSections(self: *Self) void {
_ = self;
std.debug.panic("Not implemented", .{});
}

View File

@@ -0,0 +1,20 @@
const std = @import("std");
const Self = @This();
const types = @import("types.zig");
const x86_64 = @import("x86_64.zig");
const Register = x86_64.Register;
const Type = types.Type;
pub const Value = union(enum) {
register: Register,
constant: u64,
symbol: []const u8,
/// Displacement in bytes from current value of base pointer register.
stack: i32,
cpu_flags: void,
};
c_type: Type,
value: Value,

View File

@@ -0,0 +1,31 @@
#pragma once
#define abs(value) __builtin_abs(value)
#define byteswap(value) __builtin_byteswap(value)
#define ceil(value) __builtin_ceil(value)
#define clz(value) __builtin_clz(value)
#define containerof(ptr, type, member) __builtin_containerof(ptr, type, member)
#define cos(value) __builtin_cos(value)
#define ctz(value) __builtin_ctz(value)
#define embedfile(path) __builtin_embedfile(path)
#define exp(value) __builtin_exp(value)
#define exp2(value) __builtin_exp2(value)
#define floor(value) __builtin_floor(value)
#define frameaddress() __builtin_frameaddress()
#define log(value) __builtin_log(value)
#define log10(value) __builtin_log10(value)
#define log2(value) __builtin_log2(value)
#define max(...) __builtin_max(__VA_ARGS__)
#define memcpy(dest, src, count) __builtin_memcpy(dest, src, count)
#define memmove(dest, src, count) __builtin_memmove(dest, src, count)
#define memset(value) __builtin_memset(value)
#define min(...) __builtin_min(__VA_ARGS__)
#define popcount(value) __builtin_popcount(value)
#define returnaddress() __builtin_returnaddress()
#define round(value) __builtin_round(value)
#define sin(value) __builtin_sin(value)
#define sqrt(value) __builtin_sqrt(value)
#define tan(value) __builtin_tan(value)
#define trunc(value) __builtin_trunc(value)
#define typename(type) __builtin_typename(type)
#define typeof(...) __builtin_typeof(__VA_ARGS__)

View File

@@ -0,0 +1,7 @@
#pragma once
#define alignas _Alignas
#define alignof _Alignof
#define __alignas_is_defined 1
#define __alignof_is_defined 1

View File

@@ -0,0 +1,7 @@
#pragma once
#define bool _Bool
#define true 1
#define false 0
#define __bool_true_false_are_defined 1

View File

@@ -0,0 +1,9 @@
#pragma once
typedef long ptrdiff_t;
typedef long max_align_t;
typedef unsigned long size_t;
#define NULL ((void *)0)
#define offsetof(type, member) __builtin_offsetof(type, member)

View File

@@ -0,0 +1,118 @@
#pragma once
typedef signed char int8_t;
typedef short int16_t;
typedef int int32_t;
typedef long int64_t;
typedef int8_t int_fast8_t;
typedef int16_t int_fast16_t;
typedef int32_t int_fast32_t;
typedef int64_t int_fast64_t;
typedef int8_t int_least8_t;
typedef int16_t int_least16_t;
typedef int32_t int_least32_t;
typedef int64_t int_least64_t;
typedef int64_t intmax_t;
typedef int64_t intptr_t;
typedef unsigned char uint8_t;
typedef unsigned short uint16_t;
typedef unsigned int uint32_t;
typedef unsigned long uint64_t;
typedef uint8_t uint_fast8_t;
typedef uint16_t uint_fast16_t;
typedef uint32_t uint_fast32_t;
typedef uint64_t uint_fast64_t;
typedef uint8_t uint_least8_t;
typedef uint16_t uint_least16_t;
typedef uint32_t uint_least32_t;
typedef uint64_t uint_least64_t;
typedef uint64_t uintmax_t;
typedef uint64_t uintptr_t;
#define INT8_MIN (-0x80)
#define INT16_MIN (-0x8000)
#define INT32_MIN (-0x80000000)
#define INT64_MIN (-0x8000000000000000L)
#define INT_FAST8_MIN INT8_MIN
#define INT_FAST16_MIN INT16_MIN
#define INT_FAST32_MIN INT32_MIN
#define INT_FAST64_MIN INT64_MIN
#define INT_LEAST8_MIN INT8_MIN
#define INT_LEAST16_MIN INT16_MIN
#define INT_LEAST32_MIN INT32_MIN
#define INT_LEAST64_MIN INT64_MIN
#define INTPTR_MIN INT64_MIN
#define INTMAX_MIN INT64_MIN
#define INT8_MAX (0x7F)
#define INT16_MAX (0x7FFF)
#define INT32_MAX (0x7FFFFFFF)
#define INT64_MAX (0x7FFFFFFFFFFFFFFFL)
#define INT_FAST8_MAX INT8_MAX
#define INT_FAST16_MAX INT16_MAX
#define INT_FAST32_MAX INT32_MAX
#define INT_FAST64_MAX INT64_MAX
#define INT_LEAST8_MAX INT8_MAX
#define INT_LEAST16_MAX INT16_MAX
#define INT_LEAST32_MAX INT32_MAX
#define INT_LEAST64_MAX INT64_MAX
#define INTPTR_MAX INT64_MAX
#define INTMAX_MAX INT64_MAX
#define UINT8_MAX (0xFF)
#define UINT16_MAX (0xFFFF)
#define UINT32_MAX (0xFFFFFFFFU)
#define UINT64_MAX (0xFFFFFFFFFFFFFFFFUL)
#define UINT_FAST8_MAX UINT8_MAX
#define UINT_FAST16_MAX UINT16_MAX
#define UINT_FAST32_MAX UINT32_MAX
#define UINT_FAST64_MAX UINT64_MAX
#define UINT_LEAST8_MAX UINT8_MAX
#define UINT_LEAST16_MAX UINT16_MAX
#define UINT_LEAST32_MAX UINT32_MAX
#define UINT_LEAST64_MAX UINT64_MAX
#define UINTPTR_MAX UINT64_MAX
#define UINTMAX_MAX UINT64_MAX
#define INT8_C(value) value
#define INT16_C(value) value
#define INT32_C(value) value
#define INT64_C(value) value ## L
#define INTMAX_C(value) value ## L
#define UINT8_C(value) value
#define UINT16_C(value) value
#define UINT32_C(value) value ## U
#define UINT64_C(value) value ## UL
#define UINTMAX_C(value) value ## UL
#define PTRDIFF_MIN (-0x8000000000000000L)
#define PTRDIFF_MAX (0x7FFFFFFFFFFFFFFFL)
#define SIZE_MAX (0xFFFFFFFFFFFFFFFFUL)
#define WINT_MIN (-0x80000000)
#define WINT_MAX (0x7FFFFFFF)
#define WCHAR_MIN (0)
#define WCHAR_MAX (0x10FFFF)
#endif

View File

@@ -0,0 +1,3 @@
#pragma once
#define noreturn _Noreturn

View File

@@ -0,0 +1,19 @@
const std = @import("std");
const builtin = @import("builtin");
pub const Runtime = @import("Runtime.zig");
pub const Sections = @import("Sections.zig");
pub const StackValue = @import("StackValue.zig");
pub const tokens = @import("tokens.zig");
pub const types = @import("types.zig");
pub const x86_64 = @import("x86_64.zig");
pub const call: std.builtin.CallingConvention = switch (builtin.cpu.arch) {
.aarch64 => .{ .aarch64_aapcs = .{} },
.x86_64 => .{ .x86_64_sysv = .{} },
else => @compileError("Architecture " ++ @tagName(builtin.cpu.arch) ++ " not supported"),
};
test {
std.testing.refAllDeclsRecursive(@This());
}

View File

@@ -0,0 +1,7 @@
pub const Builtin = @import("tokens/Builtin.zig").Builtin;
pub const Constant = @import("tokens/Constant.zig").Constant;
pub const Keyword = @import("tokens/Keyword.zig").Keyword;
pub const Punctuator = @import("tokens/Punctuator.zig").Punctuator;
pub const Token = @import("tokens/Token.zig").Token;
pub const Tokenizer = @import("tokens/Tokenizer.zig");
pub const Utf8Iterator = @import("tokens/Utf8Iterator.zig");

View File

@@ -0,0 +1,156 @@
const std = @import("std");
pub const Builtin = enum {
/// Usage: `__builtin_abs(value)`
///
/// `value` can be any integer or real type. Equivalent to
/// `value < 0 ? -value : value`. Noop for unsigned integer types.
__builtin_abs,
/// Usage: `__builtin_byteswap(value)`
///
/// `value` can be any integer type.
__builtin_byteswap,
/// Usage: `__builtin_ceil(value)`
///
/// `value` can be any real type.
__builtin_ceil,
/// Usage: `__builtin_clz(value)`
///
/// Count leading zeroes. `value` can be any integer type. The return type
/// is `int`.
__builtin_clz,
/// Usage `__builtin_containerof(ptr, type, member)`
///
/// `ptr` must be an pointer to a struct or a union. `type` must be a type.
/// `member` must be an identifier. Given `ptr` is a pointer to a given
/// member of `type`, returns a pointer to the entire container.
__builtin_containerof,
/// Usage: `__builtin_cos(value)`
///
/// `value` can be any real type.
__builtin_cos,
/// Usage: `__builtin_ctz(value)`
///
/// Count trailing zeroes. `value` can be any integer type. The return type
/// is `int`.
__builtin_ctz,
/// Usage: `__builtin_embedfile(path)`
///
/// `path` must be a string literal. The return type is `const char *`. The
/// data is null-terminated.
__builtin_embedfile,
/// Usage `__builtin_exp(value)`
///
/// `value` can be any real type. Calculates e^value.
__builtin_exp,
/// Usage `__builtin_exp2(value)`
///
/// `value` can be any real type. Calculates 2^value.
__builtin_exp2,
/// Usage: `__bultin_floor(value)`
///
/// `value` can be any real type.
__builtin_floor,
/// Usage: `__builtin_frameaddress()`
///
/// Returns the value of base pointer. The return type is equivalent to
/// `uintptr_t`.
__builtin_frameaddress,
/// Usage: `__builtin_log(value)`
///
/// `value` can be any real type. Calculates natural logarithm (base e).
__builtin_log,
/// Usage: `__builtin_log10(value)`
///
/// `value` can be any real type. Calculates base 10 logarithm.
__builtin_log10,
/// Usage: `__builtin_log2(value)`
///
/// `value` can be any real type. Calculates base 2 logarithm.
__builtin_log2,
/// Usage: `__builtin_max(...)`
///
/// The arguments can be any integer or real types. NaN values are ignored.
__builtin_max,
/// Usage: `__builtin_memcpy(dest, src, count)`
///
/// `dest` and `src` must be pointers. The pointers are reinterpreted as
/// pointers to `char`. `count` is coerced to the equivalent of `size_t`.
/// `dest` must be a non-const pointer. The regions must not overlap.
__builtin_memcpy,
/// Usage: `__builtin_memmove(dest, src, count)`
///
/// `dest` and `src` must be pointers. The pointers are reinterpreted as
/// pointers to `char`. `count` is coerced to the equivalent of `size_t`.
/// `dest` must be a non-const pointer. The regions may overlap.
__builtin_memmove,
/// Usage: `__builtin_memset(dest, ch, count)`
///
/// `dest` must be a pointer. The pointer is reinterpreted as pointer to
/// `char`. `ch` is cast to `unsigned char`. `dest` must be a non-const
// pointer.
__builtin_memset,
/// Usage: `__builtin_min(...)`
///
/// The arguments can be any integer or real types. NaN values are ignored.
__builtin_min,
/// Usage: `__builtin_offsetof(type, member)`
///
/// `type` must be a type. `member` must be an identifier.
__builtin_offsetof,
/// Usage: `__builtin_popcount(value)`
///
/// `value` can be any integer type. The return type is `int`.
__builtin_popcount,
/// Usage: `__builtin_returnaddress()`
///
/// Returns the address of the instruction to run after current function
/// returns. The return type is equivalent to `uintptr_t`.
__builtin_returnaddress,
/// Usage: `__builtin_round(value)`
///
/// `value` can be any real type.
__builtin_round,
/// Usage: `__builtin_sin(value)`
///
/// `value` can be any real type.
__builtin_sin,
/// Usage: `__builtin_sqrt(value)`
///
/// `value` can be any real type.
__builtin_sqrt,
/// Usage: `__builtin_tan(value)`
///
/// `value` can be any real type.
__builtin_tan,
/// Usage: `__builtin_trunc(value)`
///
/// `value` can be any real type.
__builtin_trunc,
/// Usage: `__builtin_typename(type)`
///
/// `type` must be a type. The return type is `const char *`.
__builtin_typename,
/// Usage: `__builtin_typeof(...)`
__builtin_typeof,
pub const map: std.StaticStringMap(Builtin) = blk: {
const fields = @typeInfo(Builtin).@"enum".fields;
var kvs_list: [fields.len]struct { []const u8, Builtin } = undefined;
for (fields, 0..) |field, i| {
kvs_list[i] = .{ field.name, @field(Builtin, field.name) };
}
break :blk .initComptime(kvs_list);
};
pub fn isBuiltin(identifier: []const u8) ?Builtin {
if (std.mem.startsWith(u8, identifier, "__builtin_")) {
@branchHint(.unlikely);
return map.get(identifier);
} else {
return null;
}
}
};

View File

@@ -0,0 +1,14 @@
const std = @import("std");
pub const Constant = union(enum) {
int: i32,
long: i64,
long_long: i64,
unsigned_int: u32,
unsigned_long: u64,
unsigned_long_long: u64,
float: f32,
double: f64,
character: u8,
wide_character: i32,
};

View File

@@ -0,0 +1,63 @@
const std = @import("std");
pub const Keyword = enum {
_Alignas,
_Alignof,
_Atomic,
_Bool,
_Complex,
_Generic,
_Imaginary,
_Noreturn,
_Static_assert,
_Thread_local,
auto,
@"break",
case,
char,
@"const",
@"continue",
default,
do,
double,
@"else",
@"enum",
@"extern",
float,
@"for",
goto,
@"if",
@"inline",
int,
long,
register,
restrict,
@"return",
short,
signed,
sizeof,
static,
@"struct",
@"switch",
typedef,
@"union",
unsigned,
void,
@"volatile",
@"while",
pub const map: std.StaticStringMap(Keyword) = blk: {
const fields = @typeInfo(Keyword).@"enum".fields;
var kvs_list: [fields.len]struct { []const u8, Keyword } = undefined;
for (fields, 0..) |field, i| {
kvs_list[i] = .{ field.name, @field(Keyword, field.name) };
}
break :blk .initComptime(kvs_list);
};
pub fn isKeyword(identifier: []const u8) ?Keyword {
return map.get(identifier);
}
};

View File

@@ -0,0 +1,70 @@
const std = @import("std");
pub const Punctuator = enum(u32) {
// three characters
@"..." = strToInt3("..."),
@"<<=" = strToInt3("<<="),
@">>=" = strToInt3(">>="),
// two characters
@"--" = strToInt2("--"),
@"-=" = strToInt2("-="),
@"->" = strToInt2("->"),
@"!=" = strToInt2("!="),
@"*=" = strToInt2("*="),
@"/=" = strToInt2("/="),
@"&&" = strToInt2("&&"),
@"&=" = strToInt2("&="),
@"##" = strToInt2("##"),
@"%=" = strToInt2("%="),
@"^=" = strToInt2("^="),
@"++" = strToInt2("++"),
@"+=" = strToInt2("+="),
@"<<" = strToInt2("<<"),
@"<=" = strToInt2("<="),
@"==" = strToInt2("=="),
@">=" = strToInt2(">="),
@">>" = strToInt2(">>"),
@"|=" = strToInt2("|="),
@"||" = strToInt2("||"),
// single character
@"-" = strToInt1("-"),
@"," = strToInt1(","),
@";" = strToInt1(";"),
@":" = strToInt1(":"),
@"!" = strToInt1("!"),
@"?" = strToInt1("?"),
@"." = strToInt1("."),
@"(" = strToInt1("("),
@")" = strToInt1(")"),
@"[" = strToInt1("["),
@"]" = strToInt1("]"),
@"{" = strToInt1("{"),
@"}" = strToInt1("}"),
@"*" = strToInt1("*"),
@"/" = strToInt1("/"),
@"&" = strToInt1("&"),
@"#" = strToInt1("#"),
@"%" = strToInt1("%"),
@"^" = strToInt1("^"),
@"+" = strToInt1("+"),
@"<" = strToInt1("<"),
@"=" = strToInt1("="),
@">" = strToInt1(">"),
@"|" = strToInt1("|"),
@"~" = strToInt1("~"),
pub const line_continuation_lf = strToInt2("\\\n");
pub const line_continuation_crlf = strToInt3("\\\r\n");
};
fn strToInt1(str: *const [1]u8) u32 {
return @as(u8, @bitCast(str.*));
}
fn strToInt2(str: *const [2]u8) u32 {
return @as(u16, @bitCast(str.*));
}
fn strToInt3(str: *const [3]u8) u32 {
return @as(u24, @bitCast(str.*));
}

View File

@@ -0,0 +1,16 @@
const std = @import("std");
const Builtin = @import("Builtin.zig").Builtin;
const Constant = @import("Constant.zig").Constant;
const Keyword = @import("Keyword.zig").Keyword;
const Punctuator = @import("Punctuator.zig").Punctuator;
pub const Token = union(enum) {
builtin: Builtin,
constant: Constant,
keyword: Keyword,
punctuator: Punctuator,
identifier: []const u8,
string_literal: [:0]const u8,
wide_string_literal: [:0]const u32,
};

View File

@@ -0,0 +1,263 @@
const std = @import("std");
const Self = @This();
const Builtin = @import("Builtin.zig").Builtin;
const Keyword = @import("Keyword.zig").Keyword;
const Punctuator = @import("Punctuator.zig").Punctuator;
const Token = @import("Token.zig").Token;
const Utf8Iterator = @import("Utf8Iterator.zig");
pub const max_string_length = 4096;
pub const max_wide_string_length = 4096;
filename: []const u8 = &.{},
it: Utf8Iterator = .init(&.{}),
defines: std.StringHashMapUnmanaged([]Token) = .{},
/// Bounded, preallocated with the capacity of `max_string_length`.
string: std.ArrayList(u8),
/// Bounded, preallocated with the capacity of `max_wide_string_length`.
wide_string: std.ArrayList(u32),
pub fn init(arena_allocator: std.mem.Allocator) !Self {
const string_buffer = try arena_allocator.alloc(u8, max_string_length);
const wide_string_buffer = try arena_allocator.alloc(u32, max_wide_string_length);
return .{
.string = .initBuffer(string_buffer),
.wide_string = .initBuffer(wide_string_buffer),
};
}
pub fn setSource(self: *Self, filename: []const u8, code: []const u8) void {
self.filename = filename;
self.it = .init(code);
}
pub fn nextToken(self: *Self, arena_allocator: std.mem.Allocator) !?Token {
try self.skipWhitespace();
// TODO Skip C and C++ style comments
// TODO Preprocessor directives
const cp = try self.peekCodepointSkipLineContinuation() orelse return null;
switch (cp) {
// Identifier start
'A'...'Z', '_', 'a'...'z', 128...std.math.maxInt(u21) => {
// This is an identifier, with the possible exception of:
// - wide string: L"
// - wide char: L'
// - any keyword
if (cp == 'L') {
const state = self.it.save();
self.it.advanceCodepoint(cp);
const cp2 = try self.peekCodepointSkipLineContinuation() orelse 0;
switch (cp2) {
// Wide string
'\"' => {
self.it.advanceCodepoint(cp2);
self.wide_string.clearRetainingCapacity();
// TODO Parse wide string
},
// Wide char
'\'' => {
self.it.advanceCodepoint(cp2);
// TODO Parse wide char
},
// Identifier or keyword
else => {
self.it.restore(state);
},
}
}
const identifier_start = self.it.ptr;
self.it.advanceCodepoint(cp);
var next_cp = try self.peekCodepointSkipLineContinuation();
while (next_cp != null and isIdentifierMiddle(next_cp.?)) {
self.it.advanceCodepoint(next_cp.?);
next_cp = try self.peekCodepointSkipLineContinuation();
}
const identifier = self.it.str[identifier_start..self.it.ptr];
// TODO Preprocessor
if (Keyword.isKeyword(identifier)) |keyword| {
return .{ .keyword = keyword };
} else if (Builtin.isBuiltin(identifier)) |builtin| {
return .{ .builtin = builtin };
} else {
return .{ .identifier = try arena_allocator.dupe(u8, identifier) };
}
},
// String
'\"' => {
self.it.advanceCodepoint(cp);
self.string.clearRetainingCapacity();
// TODO Parse string
},
// Char
'\'' => {
self.it.advanceCodepoint(cp);
// TODO Parse char
},
else => {},
}
// Higher code points should've been already handled. The code below may
// assume that `cp` is an ASCII character.
std.debug.assert(cp < 128);
// TODO Numeric constants
const cp3 = self.it.peekThreeBytes().?;
switch (cp3 & 0x00_FF_FF_FF) {
inline @intFromEnum(Punctuator.@"..."),
@intFromEnum(Punctuator.@"<<="),
@intFromEnum(Punctuator.@">>="),
=> |p| {
self.it.ptr += 3;
self.it.col += 3;
return .{
.punctuator = @enumFromInt(p),
};
},
else => {},
}
switch (cp3 & 0x00_00_FF_FF) {
inline @intFromEnum(Punctuator.@"--"),
@intFromEnum(Punctuator.@"-="),
@intFromEnum(Punctuator.@"->"),
@intFromEnum(Punctuator.@"!="),
@intFromEnum(Punctuator.@"*="),
@intFromEnum(Punctuator.@"/="),
@intFromEnum(Punctuator.@"&&"),
@intFromEnum(Punctuator.@"&="),
@intFromEnum(Punctuator.@"##"),
@intFromEnum(Punctuator.@"%="),
@intFromEnum(Punctuator.@"^="),
@intFromEnum(Punctuator.@"++"),
@intFromEnum(Punctuator.@"+="),
@intFromEnum(Punctuator.@"<<"),
@intFromEnum(Punctuator.@"<="),
@intFromEnum(Punctuator.@"=="),
@intFromEnum(Punctuator.@">="),
@intFromEnum(Punctuator.@">>"),
@intFromEnum(Punctuator.@"|="),
@intFromEnum(Punctuator.@"||"),
=> |p| {
self.it.ptr += 2;
self.it.col += 2;
return .{
.punctuator = @enumFromInt(p),
};
},
else => {},
}
switch (cp3 & 0x00_00_00_FF) {
inline @intFromEnum(Punctuator.@"-"),
@intFromEnum(Punctuator.@","),
@intFromEnum(Punctuator.@";"),
@intFromEnum(Punctuator.@":"),
@intFromEnum(Punctuator.@"!"),
@intFromEnum(Punctuator.@"?"),
@intFromEnum(Punctuator.@"."),
@intFromEnum(Punctuator.@"("),
@intFromEnum(Punctuator.@")"),
@intFromEnum(Punctuator.@"["),
@intFromEnum(Punctuator.@"]"),
@intFromEnum(Punctuator.@"{"),
@intFromEnum(Punctuator.@"}"),
@intFromEnum(Punctuator.@"*"),
@intFromEnum(Punctuator.@"/"),
@intFromEnum(Punctuator.@"&"),
@intFromEnum(Punctuator.@"#"),
@intFromEnum(Punctuator.@"%"),
@intFromEnum(Punctuator.@"^"),
@intFromEnum(Punctuator.@"+"),
@intFromEnum(Punctuator.@"<"),
@intFromEnum(Punctuator.@"="),
@intFromEnum(Punctuator.@">"),
@intFromEnum(Punctuator.@"|"),
@intFromEnum(Punctuator.@"~"),
=> |p| {
self.it.ptr += 1;
self.it.col += 1;
return .{
.punctuator = @enumFromInt(p),
};
},
else => {},
}
return error.InvalidToken;
}
fn peekCodepointSkipLineContinuation(self: *Self) !?u21 {
while (self.skipLineContinuation()) {}
const cp = try self.it.peekCodepoint();
return cp;
}
/// Line continuation is defined as a backslash followed imediatelly by LF or
/// CRLF. Return whether a line continuation was encountered and therefore
/// skipped past.
fn skipLineContinuation(self: *Self) bool {
if (self.it.peekThreeBytes()) |b| {
@branchHint(.likely);
if (b & 0x00_00_FF_FF == Punctuator.line_continuation_lf) {
@branchHint(.unlikely);
self.it.ptr += 2;
self.it.line += 1;
self.it.col = 1;
return true;
} else if (b & 0x00_FF_FF_FF == Punctuator.line_continuation_crlf) {
@branchHint(.unlikely);
self.it.ptr += 3;
self.it.line += 1;
self.it.col = 1;
return true;
}
}
return false;
}
fn skipWhitespace(self: *Self) !void {
while (try self.peekCodepointSkipLineContinuation()) |cp| {
switch (cp) {
// <Character Tabulation> (HT, TAB)
0x0009,
// <End of Line> (EOL, LF, NL)
0x000A,
// <Line Tabulation> (VT)
0x000B,
// <Form Feed> (FF)
0x000C,
// <Carriage Return> (CR)
0x000D,
// Space (SP)
0x0020,
=> self.it.advanceCodepoint(cp),
else => return,
}
}
}
fn isIdentifierMiddle(code_point: u21) bool {
// zig fmt: off
return code_point >= '0' and code_point <= '9'
or code_point >= 'A' and code_point <= 'Z'
or code_point == '_'
or code_point >= 'a' and code_point <= 'z'
or code_point >= 128;
// zig fmt: on
}

View File

@@ -0,0 +1,99 @@
const std = @import("std");
const Self = @This();
str: []const u8,
ptr: usize,
line: usize,
col: usize,
pub const State = struct {
ptr: usize,
line: usize,
col: usize,
};
pub fn init(str: []const u8) Self {
return .{
.str = str,
.ptr = 0,
.line = 1,
.col = 1,
};
}
pub fn save(self: Self) State {
return .{
.ptr = self.ptr,
.line = self.line,
.col = self.col,
};
}
pub fn restore(self: *Self, state: State) void {
self.ptr = state.ptr;
self.line = state.line;
self.col = state.col;
}
pub fn peekByte(self: *Self) ?u8 {
if (self.ptr >= self.str.len) {
return null;
}
return self.str[self.ptr];
}
pub fn peekCodepoint(self: Self) !?u21 {
if (self.ptr >= self.str.len) {
return null;
}
const cp_len = std.unicode.utf8ByteSequenceLength(self.str[self.ptr]) catch return error.InvalidUtf8;
if (self.ptr + cp_len > self.str.len) return error.InvalidUtf8;
const cp_slice = self.str[self.ptr .. self.ptr + cp_len];
const cp = std.unicode.utf8Decode(cp_slice) catch return error.InvalidUtf8;
return cp;
}
pub fn peekThreeBytes(self: Self) ?u32 {
var bytes: [3]u8 = .{ 0, 0, 0 };
const bytes_left = self.str.len - self.ptr;
sw: switch (bytes_left) {
0 => return null,
1 => {
bytes[0] = self.str[self.ptr];
return @as(u24, @bitCast(bytes));
},
2 => {
bytes[1] = self.str[self.ptr + 1];
continue :sw 1;
},
else => {
bytes[2] = self.str[self.ptr + 2];
continue :sw 2;
},
}
}
/// Call with value returned by `peekCodepoint`.
pub fn advanceCodepoint(self: *Self, cp: u21) void {
std.debug.assert(blk: {
const actual_cp = self.peekCodepoint() catch break :blk false;
break :blk cp == actual_cp;
});
const cp_len = std.unicode.utf8CodepointSequenceLength(cp) catch unreachable;
self.ptr += cp_len;
if (cp == '\n') {
self.line += 1;
// NOTE Columns start as 1, it will be incremented below.
self.col = 0;
}
self.col += 1;
}

201
packages/cjit/src/types.zig Normal file
View File

@@ -0,0 +1,201 @@
const std = @import("std");
pub const Type = union(enum) {
signed_char: void,
signed_short: void,
signed_int: void,
signed_long: void,
signed_long_long: void,
unsigned_char: void,
unsigned_short: void,
unsigned_int: void,
unsigned_long: void,
unsigned_long_long: void,
float: void,
double: void,
long_double: void,
void: void,
noreturn: void,
char: void,
bool: void,
@"enum": *const Enum,
@"struct": *const Struct,
@"union": *const Union,
array: *const Array,
function: *const Function,
pointer: *const Pointer,
pub fn sizeOf(self: Type) ?usize {
return switch (self) {
.signed_char => 1,
.signed_short => 2,
.signed_int => 4,
.signed_long => 8,
.signed_long_long => 8,
.unsigned_char => 1,
.unsigned_short => 2,
.unsigned_int => 4,
.unsigned_long => 8,
.unsigned_long_long => 8,
.float => 4,
.double => 8,
.long_double => 8,
.void => null,
.noreturn => null,
.char => 1,
.bool => 1,
.@"enum" => 4,
.@"struct" => |s| s.size,
.@"union" => |u| u.size,
.array => |a| if (a.length) |l| (if (sizeOf(a.child)) |c| l * c else null) else 8,
.function => null,
.pointer => 8,
};
}
pub fn alignOf(self: Type) ?u16 {
return switch (self) {
.signed_char => 1,
.signed_short => 2,
.signed_int => 4,
.signed_long => 8,
.signed_long_long => 8,
.unsigned_char => 1,
.unsigned_short => 2,
.unsigned_int => 4,
.unsigned_long => 8,
.unsigned_long_long => 8,
.float => 4,
.double => 8,
.long_double => 8,
.void => null,
.noreturn => null,
.char => 1,
.bool => 1,
.@"enum" => 4,
.@"struct" => |s| s.@"align",
.@"union" => |u| u.@"align",
.array => |a| if (a.length != null) alignOf(a.child) else 8,
.function => null,
.pointer => 8,
};
}
};
pub const Enum = struct {
name: []const u8,
constants: []const EnumConstant,
};
pub const EnumConstant = struct {
name: []const u8,
value: i32,
inferred: bool,
};
pub const Struct = struct {
name: []const u8,
fields: []const StructField,
size: usize,
@"align": u16,
};
pub const StructField = struct {
name: []const u8,
type: Type,
offset: usize,
};
pub const Union = struct {
name: []const u8,
fields: []const UnionField,
size: usize,
@"align": u16,
};
pub const UnionField = struct {
name: []const u8,
type: Type,
};
pub const Array = struct {
child: Type,
length: ?usize,
};
pub const Function = struct {
arguments: []const FunctionArgument,
@"return": Type,
};
pub const FunctionArgument = struct {
name: []const u8,
type: Type,
};
pub const Pointer = struct {
child: Type,
@"const": bool,
@"volatile": bool,
};
pub fn isCompatible(comptime ZigType: type, c_type: Type) bool {
return switch (@typeInfo(ZigType)) {
.type => false,
.void => c_type == .void,
.bool => c_type == .bool,
.noreturn => c_type == .noreturn,
.int => |zig_int| switch (c_type) {
.signed_char => zig_int.signedness == .signed and zig_int.bits == 8,
.signed_short => zig_int.signedness == .signed and zig_int.bits == 16,
.signed_int => zig_int.signedness == .signed and zig_int.bits == 32,
.signed_long => zig_int.signedness == .signed and zig_int.bits == 64,
.signed_long_long => zig_int.signedness == .signed and zig_int.bits == 64,
.unsigned_char => zig_int.signedness == .unsigned and zig_int.bits == 8,
.unsigned_short => zig_int.signedness == .unsigned and zig_int.bits == 16,
.unsigned_int => zig_int.signedness == .unsigned and zig_int.bits == 32,
.unsigned_long => zig_int.signedness == .unsigned and zig_int.bits == 64,
.unsigned_long_long => zig_int.signedness == .unsigned and zig_int.bits == 64,
.char => zig_int.bits = 8,
else => false,
},
.float => |zig_float| switch (c_type) {
.float => zig_float.bits = 32,
.double => zig_float.bits = 64,
.long_double => zig_float.bits = 64,
},
.pointer => @compileError("TODO"),
.array => @compileError("TODO"),
.@"struct" => @compileError("TODO"),
.comptime_float => false,
.comptime_int => false,
.undefined => true,
.null => @compileError("TODO"),
.optional => @compileError("TODO"),
.error_union => false,
.error_set => false,
.@"enum" => @compileError("TODO"),
.@"union" => @compileError("TODO"),
.@"fn" => false,
.@"opaque" => @compileError("TODO"),
.frame => false,
.@"anyframe" => false,
.vector => false,
.enum_literal => false,
};
}

View File

@@ -0,0 +1,445 @@
const std = @import("std");
const tokens = @import("tokens.zig");
const types = @import("types.zig");
const Location = Runtime.Location;
const Runtime = @import("Runtime.zig");
const StackValue = @import("StackValue.zig");
const Type = types.Type;
pub const Register = union(enum) {
gpr: Gpr,
xmm: Xmm,
};
pub const Gpr = enum(u4) {
rax = 0,
rcx = 1,
rdx = 2,
rbx = 3,
rsp = 4,
rbp = 5,
rsi = 6,
rdi = 7,
r8 = 8,
r9 = 9,
r10 = 10,
r11 = 11,
r12 = 12,
r13 = 13,
r14 = 14,
r15 = 15,
pub fn reg(self: Gpr) u3 {
return @truncate(@intFromEnum(self));
}
pub fn x(self: Gpr) bool {
return @intFromEnum(self) & 0b1000 != 0;
}
};
pub const Xmm = enum(u4) {
xmm0 = 0,
xmm1 = 1,
xmm2 = 2,
xmm3 = 3,
xmm4 = 4,
xmm5 = 5,
xmm6 = 6,
xmm7 = 7,
xmm8 = 8,
xmm9 = 9,
xmm10 = 10,
xmm11 = 11,
xmm12 = 12,
xmm13 = 13,
xmm14 = 14,
xmm15 = 15,
pub fn reg(self: Xmm) u3 {
return @truncate(@intFromEnum(self));
}
pub fn x(self: Xmm) bool {
return @intFromEnum(self) & 0b1000 != 0;
}
};
// --- EMIT HELPERS ------------------------------------------------------------
pub const Rex = packed struct(u8) {
b: bool = false,
x: bool = false,
r: bool = false,
w: bool = false,
/// MUST always be the default value
prefix: u4 = 0b0100,
};
pub const ModRM = packed struct(u8) {
rm: u3,
reg: u3,
mod: u2,
};
pub const SIB = packed struct(u8) {
base: u3,
index: u3,
scale: u2,
};
pub fn op16(rt: *Runtime) !void {
try rt.sections.text.writeByte(0x66, rt.allocator);
}
pub fn rex(rt: *Runtime, value: Rex) !void {
try rt.sections.text.writeByte(@bitCast(value), rt.allocator);
}
pub fn op(rt: *Runtime, value: u8) !void {
try rt.sections.text.writeByte(value, rt.allocator);
}
pub fn op2(rt: *Runtime, v0: u8, v1: u8) !void {
const bytes: [2]u8 = .{ v0, v1 };
try rt.sections.text.writeBytes(&bytes, rt.allocator);
}
pub fn op3(rt: *Runtime, v0: u8, v1: u8, v2: u8) !void {
const bytes: [3]u8 = .{ v0, v1, v2 };
try rt.sections.text.writeBytes(&bytes, rt.allocator);
}
pub fn modrm(rt: *Runtime, value: ModRM) !void {
try rt.sections.text.writeByte(@bitCast(value), rt.allocator);
}
pub fn sib(rt: *Runtime, value: SIB) !void {
try rt.sections.text.writeByte(@bitCast(value), rt.allocator);
}
pub fn imm8(rt: *Runtime, value: u8) !void {
try rt.sections.text.writeByte(value, rt.allocator);
}
pub fn imm16(rt: *Runtime, value: u16) !void {
var bytes: [2]u8 = undefined;
std.mem.writeInt(u16, &bytes, value, .little);
try rt.sections.text.writeBytes(&bytes, rt.allocator);
}
pub fn imm32(rt: *Runtime, value: u32) !void {
var bytes: [4]u8 = undefined;
std.mem.writeInt(u32, &bytes, value, .little);
try rt.sections.text.writeBytes(&bytes, rt.allocator);
}
pub fn imm64(rt: *Runtime, value: u64) !void {
var bytes: [8]u8 = undefined;
std.mem.writeInt(u64, &bytes, value, .little);
try rt.sections.text.writeBytes(&bytes, rt.allocator);
}
pub fn disp8(rt: *Runtime, value: i8) !void {
try rt.sections.text.writeByte(@bitCast(value), rt.allocator);
}
pub fn disp32(rt: *Runtime, value: i32) !void {
var bytes: [4]u8 = undefined;
std.mem.writeInt(i32, &bytes, value, .little);
try rt.sections.text.writeBytes(&bytes, rt.allocator);
}
// -----------------------------------------------------------------------------
pub const Operation = enum {
add,
bit_and,
bit_not,
bit_or,
bit_xor,
bool_and,
bool_not,
bool_or,
cmp_eq,
cmp_gt,
cmp_gte,
cmp_lt,
cmp_lte,
cmp_neq,
div,
mod,
mul,
neg,
sar,
shl,
shr,
sub,
};
pub fn opFloat(rt: *Runtime, operation: Operation) !void {
_ = rt;
_ = operation;
}
pub fn opInt(rt: *Runtime, operation: Operation) !void {
_ = rt;
_ = operation;
}
pub fn cvtIntToFloat(rt: *Runtime) !void {
_ = rt;
}
pub fn cvtFloatToInt(rt: *Runtime) !void {
_ = rt;
}
pub fn cvtFloatToFloat(rt: *Runtime, target: Type) !void {
const top = vsTop(rt);
switch (top.c_type) {
.float => switch (target) {
.float => {
// do nothing
},
.double, .long_double => {
// CVTSS2SD xmm1, xmm2/m32
// F3 0F 5A /r
},
else => unreachable,
},
.double, .long_double => switch (target) {
.float => {
// CVTSD2SS xmm1, xmm2/m64
// F2 0F 5A /r
},
.double, .long_double => {
// do nothing
},
else => unreachable,
},
else => unreachable,
}
}
// --- LOAD AND STORE ----------------------------------------------------------
/// Load value into register. The value must be 1, 2, 4 or 8 bytes long.
pub fn load(rt: *Runtime, dst_register: Register, src_value: *const StackValue) !void {
const size = src_value.c_type.sizeOf().?;
std.debug.assert(size == 1 or size == 2 or size == 4 or size == 8);
switch (src_value.value) {
.register => |src_register| {
if (std.meta.eql(dst_register, src_register)) return;
// TODO
},
.constant => |constant| switch (dst_register) {
.gpr => |dest_gpr| switch (size) {
1 => {
// MOV r8, imm8
// B0+ rb ib
if (@intFromEnum(dest_gpr) >= 4) {
// NOTE spl, bpl, sil and dil need an empty REX prefix,
// otherwise ah, ch, dh and bh would be used.
try rex(rt, .{ .r = dest_gpr.x() });
}
try op(rt, 0xB0 | @as(u8, dest_gpr.reg()));
try imm8(rt, @truncate(constant));
},
2 => {
// MOV r16, imm16
// B8+ rw iw
try op16(rt);
if (@intFromEnum(dest_gpr) >= 8) {
try rex(rt, .{ .r = dest_gpr.x() });
}
try op(rt, 0xB8 | @as(u8, dest_gpr.reg()));
try imm16(rt, @truncate(constant));
},
4 => {
// MOV r32, imm32
// B8+ rd id
if (@intFromEnum(dest_gpr) >= 8) {
try rex(rt, .{ .r = dest_gpr.x() });
}
try op(rt, 0xB8 | @as(u8, dest_gpr.reg()));
try imm32(rt, @truncate(constant));
},
8 => {
// MOV r64, imm64
// REX.W + B8+ rd io
try rex(rt, .{ .r = dest_gpr.x(), .w = true });
try op(rt, 0xB8 | @as(u8, dest_gpr.reg()));
try imm64(rt, constant);
},
else => unreachable,
},
.xmm => |dest_xmm| {
var bytes: [8]u8 = undefined;
std.mem.writeInt(u64, &bytes, @truncate(constant), .little);
const data = bytes[0..size];
// MOVD xmm, r/m32
// 66 0F 6E /r
// MOVQ xmm, r/m64
// 66 REX.W 0F 6E /r
try op(rt, 0x66);
if (@intFromEnum(dest_xmm) >= 8 or size == 8) {
try rex(rt, .{ .r = dest_xmm.x(), .w = size == 8 });
}
try op2(rt, 0x0F, 0x6E);
// [rip + disp32]
try modrm(rt, .{ .mod = 0b00, .reg = dest_xmm.reg(), .rm = Gpr.rbp.reg() });
try allocRodataDisp32(rt, data);
},
},
.symbol => |symbol| {},
.stack => |disp| {
const disp_small = std.math.minInt(i8) <= disp and disp <= std.math.maxInt(i8);
const rex_prefix: Rex = .{
.r = switch (dst_register) {
.gpr => |dest_gpr| dest_gpr.x(),
.xmm => |dest_xmm| dest_xmm.x(),
},
.w = size == 8,
};
const mod: u2 = if (disp_small) 0b01 else 0b10;
const reg: u3 = switch (dst_register) {
.gpr => |dest_gpr| dest_gpr.reg(),
.xmm => |dest_xmm| dest_xmm.reg(),
};
switch (dst_register) {
.gpr => |dest_gpr| {
switch (size) {
1 => {
// MOV r8, r/m8
// 8A /r
if (@intFromEnum(dest_gpr) >= 4) {
// NOTE spl, bpl, sil and dil need an empty REX prefix,
// otherwise ah, ch, dh and bh would be used.
try rex(rt, rex_prefix);
}
try op(rt, 0x8A);
},
2 => {
// MOV r16, r/m16
// 8B /r
try op16(rt);
if (@intFromEnum(dest_gpr) >= 8) {
try rex(rt, rex_prefix);
}
try op(rt, 0x8B);
},
4 => {
// MOV r32, r/m32
// 8B /r
if (@intFromEnum(dest_gpr) >= 8) {
try rex(rt, rex_prefix);
}
try op(rt, 0x8B);
},
8 => {
// MOV r64, r/m64
// REX.W + 8B /r
try rex(rt, rex_prefix);
try op(rt, 0x8B);
},
else => unreachable,
}
},
.xmm => |dest_xmm| {
// MOVD xmm, r/m32
// 66 0F 6E /r
// MOVQ xmm, r/m64
// 66 REX.W 0F 6E /r
try op(rt, 0x66);
if (@intFromEnum(dest_xmm) >= 8 or size == 8) {
try rex(rt, rex_prefix);
}
try op2(rt, 0x0F, 0x6E);
},
}
// [rbp + disp8/32]
try modrm(rt, .{ .mod = mod, .reg = reg, .rm = Gpr.rbp.reg() });
if (disp_small) {
try disp8(rt, @intCast(disp));
} else {
try disp32(rt, disp);
}
},
.cpu_flags => {},
}
}
/// Store register into value.
pub fn store(rt: *Runtime, dst_value: *const StackValue, src_register: Register) !void {
const size = dst_value.c_type.sizeOf().?;
std.debug.assert(size == 1 or size == 2 or size == 4 or size == 8);
}
// --- STACK OPERATIONS --------------------------------------------------------
/// Caller asserts that the stack is not empty.
pub fn vsTop(rt: *Runtime) *StackValue {
const vs = rt.virtual_stack.items;
return &vs[vs.len - 1];
}
/// Caller asserts that the stack has at least two values.
pub fn vsSwap(rt: *Runtime) void {
const vs = rt.virtual_stack.items;
std.mem.swap(StackValue, &vs[vs.len - 1], &vs[vs.len - 2]);
}
/// Ensure the top of the stack is in an XMM register. Returns the id of the
/// register. Caller asserts that the stack is not empty.
pub fn vsEnsureXmm(rt: *Runtime) Xmm {
const top = vsTop(rt);
switch (top.value) {
.register => {},
.constant => {},
.symbol => {},
.stack => {},
.cpu_flags => {},
}
}
/// Ensure the top of the stack is in a GPR register. Returns the id of the
/// register. Caller asserts that teh stack is not empty.
pub fn vsEnsureGpr(rt: *Runtime) Gpr {
const top = vsTop(rt);
switch (top.value) {
.register => {},
.constant => {},
.symbol => {},
.stack => {},
.cpu_flags => {},
}
}
// --- DATA ALLOCATIONS --------------------------------------------------------
/// Reserve `data.len` bytes in rodata and fill it with `data`, then add a
/// placeholder dips32 part of an instruction and a relocation entry for it.
pub fn allocRodataDisp32(rt: *Runtime, data: []const u8) !void {
const addr = rt.sections.text.data.items.len;
const location: Location = .{ .rodata = rt.sections.rodata.data.items.len };
try rt.sections.rodata.writeBytes(data, rt.allocator);
try disp32(rt, 0);
try rt.relocation_table.append(rt.allocator, .{
.addr = addr,
.location = location,
.type = .rip_disp32,
});
}

View File

@@ -0,0 +1,32 @@
const std = @import("std");
const cjit = @import("cjit");
fn add(a: i32, b: i32) callconv(cjit.call) i32 {
return a + b;
}
test {
var rt: cjit.Runtime = try .init(std.testing.allocator);
defer rt.deinit();
try rt.compile("test.c",
\\int add(int a, int b);
\\
\\int add_one(int x)
\\{
\\ return add(x, 1);
\\}
);
try rt.setSymbol(fn (i32, i32) callconv(cjit.call) i32, "add", &add);
try rt.link();
const add_one = rt.getSymbol(fn (i32) callconv(cjit.call) i32, "add_one").?;
try std.testing.expectEqual(-9, add_one(-10));
try std.testing.expectEqual(11, add_one(10));
const add_ptr = rt.getSymbol(fn (i32, i32) callconv(cjit.call) i32, "add").?;
try std.testing.expectEqual(add_ptr, &add);
}

View File

@@ -6,6 +6,18 @@ pub fn build(b: *std.Build) void {
const root_module = b.addModule("media", .{ const root_module = b.addModule("media", .{
.root_source_file = b.path("src/root.zig"), .root_source_file = b.path("src/root.zig"),
.link_libc = true,
}); });
root_module.addCSourceFile(.{
.file = b.path("src/stbi/stb_image.c"),
.flags = &.{
"-std=c17",
"-Wall",
"-Wextra",
},
.language = .c,
});
root_module.addImport("vecmath", vm_module); root_module.addImport("vecmath", vm_module);
} }

View File

@@ -66,3 +66,46 @@ pub const Dynamic = struct {
@memset(self.data[0 .. self.width * self.height], color); @memset(self.data[0 .. self.width * self.height], color);
} }
}; };
pub const Hdr = struct {
width: u32,
height: u32,
data: [*]vm.ColorHdr,
pub fn initBuffer(width: u32, height: u32, buffer: []vm.ColorHdr) @This() {
std.debug.assert(buffer.len == width * height);
return .{
.width = width,
.height = height,
.data = buffer.ptr,
};
}
pub fn initAlloc(width: u32, height: u32, allocator: std.mem.Allocator) !@This() {
const buffer = try allocator.alloc(vm.ColorHdr, width * height);
return .{
.width = width,
.height = height,
.data = buffer.ptr,
};
}
pub fn deinit(self: *@This(), allocator: std.mem.Allocator) void {
allocator.free(self.data[0 .. self.width * self.height]);
}
pub fn getPixel(self: *const @This(), x: u32, y: u32) vm.ColorHdr {
std.debug.assert(x < self.width and y < self.height);
return self.data[y * self.width + x];
}
pub fn setPixel(self: *@This(), x: u32, y: u32, color: vm.ColorHdr) void {
std.debug.assert(x < self.width and y < self.height);
self.data[y * self.width + x] = color;
}
pub fn fill(self: *@This(), color: vm.ColorHdr) void {
@memset(self.data[0 .. self.width * self.height], color);
}
};

View File

@@ -2,3 +2,4 @@ pub const audio = @import("audio.zig");
pub const image = @import("image.zig"); pub const image = @import("image.zig");
pub const qoa = @import("qoa.zig"); pub const qoa = @import("qoa.zig");
pub const qoi = @import("qoi.zig"); pub const qoi = @import("qoi.zig");
pub const stbi = @import("stbi.zig");

303
packages/media/src/stbi.zig Normal file
View File

@@ -0,0 +1,303 @@
const std = @import("std");
const Self = @This();
const image = @import("image.zig");
const vm = @import("vecmath");
allocator: std.mem.Allocator,
allocations: std.AutoHashMapUnmanaged(*anyopaque, usize) = .empty,
mutex: std.Thread.Mutex = .{},
allocated_bytes: usize = 0,
const alignment: std.mem.Alignment = .@"16";
const VoidPtr = ?*align(alignment.toByteUnits()) anyopaque;
const log = std.log.scoped(.stbi);
pub fn init(allocator: std.mem.Allocator) Self {
return .{
.allocator = allocator,
};
}
pub fn deinit(self: *Self) void {
std.log.scoped(.deinit).debug("Deinitializing {*}", .{self});
if (self.allocated_bytes > 0) {
log.warn("{d} byte(s) still allocated while deinitializing", .{self.allocated_bytes});
}
if (self.allocations.size > 0) {
log.warn("{d} allocation(s) still tracked while deinitializing", .{self.allocations.size});
var it = self.allocations.iterator();
var index: usize = 0;
while (it.next()) |entry| : (index += 1) {
log.warn("Leaked allocation ({d}/{d}) at 0x{x} of {d} byte(s)", .{
index + 1,
self.allocations.size,
@intFromPtr(entry.key_ptr.*),
entry.value_ptr.*,
});
const memory = @as([*]align(alignment.toByteUnits()) u8, @ptrCast(@alignCast(entry.key_ptr.*)))[0..entry.value_ptr.*];
self.allocator.free(memory);
}
}
self.allocations.deinit(self.allocator);
}
const import = struct {
extern fn stbi_load_from_memory(buffer: [*]const u8, len: i32, x: ?*i32, y: ?*i32, channels_in_file: ?*i32, desired_channels: i32) ?[*]align(alignment.toByteUnits()) u8;
extern fn stbi_load_16_from_memory(buffer: [*]const u8, len: i32, x: ?*i32, y: ?*i32, channels_in_file: ?*i32, desired_channels: i32) ?[*]align(alignment.toByteUnits()) u16;
extern fn stbi_loadf_from_memory(buffer: [*]const u8, len: i32, x: ?*i32, y: ?*i32, channels_in_file: ?*i32, desired_channels: i32) ?[*]align(alignment.toByteUnits()) f32;
extern fn stbi_load_from_callbacks(callbacks: *const IoCallbacks, ctx: ?*anyopaque, x: ?*i32, y: ?*i32, channels_in_file: ?*i32, desired_channels: i32) ?[*]align(alignment.toByteUnits()) u8;
extern fn stbi_load_16_from_callbacks(callbacks: *const IoCallbacks, ctx: ?*anyopaque, x: ?*i32, y: ?*i32, channels_in_file: ?*i32, desired_channels: i32) ?[*]align(alignment.toByteUnits()) u16;
extern fn stbi_loadf_from_callbacks(callbacks: *const IoCallbacks, ctx: ?*anyopaque, x: ?*i32, y: ?*i32, channels_in_file: ?*i32, desired_channels: i32) ?[*]align(alignment.toByteUnits()) f32;
extern fn stbi_info_from_memory(buffer: [*]const u8, len: i32, x: ?*i32, y: ?*i32, channels_in_file: ?*i32) i32;
extern fn stbi_is_16_bit_from_memory(buffer: [*]const u8, len: i32) i32;
extern fn stbi_is_hdr_from_memory(buffer: [*]const u8, len: i32) i32;
extern fn stbi_info_from_callbacks(callbacks: *const IoCallbacks, ctx: ?*anyopaque, x: ?*i32, y: ?*i32, channels_in_file: ?*i32) i32;
extern fn stbi_is_16_bit_from_callbacks(callbacks: *const IoCallbacks, ctx: ?*anyopaque) i32;
extern fn stbi_is_hdr_from_callbacks(callbacks: *const IoCallbacks, ctx: ?*anyopaque) i32;
};
pub fn loadStaticBuf(self: *Self, comptime W: u32, comptime H: u32, buf: []const u8) !image.Static(W, H) {
current_self = self;
defer current_self = undefined;
var x: i32 = undefined;
var y: i32 = undefined;
const res = import.stbi_load_from_memory(buf.ptr, @intCast(buf.len), &x, &y, null, 4) orelse return error.StbiError;
defer castle_media_stbi_free(res);
if (x != W or y != H) return error.WrongDimensions;
return .{ .data = @as(*const [W * H]vm.Color, @ptrCast(@alignCast(res))).* };
}
pub fn loadStaticIo(self: *Self, comptime W: u32, comptime H: u32, reader: *std.io.Reader) !image.Static(W, H) {
current_self = self;
defer current_self = undefined;
var x: i32 = undefined;
var y: i32 = undefined;
const res = import.stbi_load_from_callbacks(&.std_io_reader_interface, reader, &x, &y, null, 4) orelse return error.StbiError;
defer castle_media_stbi_free(res);
if (x != W or y != H) return error.WrongDimensions;
return .{ .data = @as(*const [W * H]vm.Color, @ptrCast(@alignCast(res))).* };
}
/// On success, must free memory by calling `freeDynamic` method.
pub fn loadDynamicBuf(self: *Self, buf: []const u8) !image.Dynamic {
current_self = self;
defer current_self = undefined;
var x: i32 = undefined;
var y: i32 = undefined;
const res = import.stbi_load_from_memory(buf.ptr, @intCast(buf.len), &x, &y, null, 4) orelse return error.StbiError;
const buffer_ptr: [*]vm.Color = @ptrCast(@alignCast(res));
const ux: u32 = @intCast(x);
const uy: u32 = @intCast(y);
return .initBuffer(ux, uy, buffer_ptr[0 .. ux * uy]);
}
/// On success, must free memory by calling `freeDynamic` method.
pub fn loadDynamicIo(self: *Self, reader: *std.io.Reader) !image.Dynamic {
current_self = self;
defer current_self = undefined;
var x: i32 = undefined;
var y: i32 = undefined;
const res = import.stbi_load_from_callbacks(&.std_io_reader_interface, reader, &x, &y, null, 4) orelse return error.StbiError;
const buffer_ptr: [*]vm.Color = @ptrCast(@alignCast(res));
const ux: u32 = @intCast(x);
const uy: u32 = @intCast(y);
return .initBuffer(ux, uy, buffer_ptr[0 .. ux * uy]);
}
/// On success, must free memory by calling `freeHdr` method.
pub fn loadHdrBuf(self: *Self, buf: []const u8) !image.Hdr {
current_self = self;
defer current_self = undefined;
var x: i32 = undefined;
var y: i32 = undefined;
const res_f32 = import.stbi_loadf_from_memory(buf.ptr, @intCast(buf.len), &x, &y, null, 4) orelse return error.StbiError;
defer castle_media_stbi_free(res_f32);
const ux: u32 = @intCast(x);
const uy: u32 = @intCast(y);
const buffer_ptr_f32: [*]vm.Vector4 = @ptrCast(res_f32);
const buffer_ptr_f16: [*]vm.ColorHdr = @ptrCast(castle_media_stbi_malloc(ux * uy * @sizeOf(vm.ColorHdr)) orelse return error.OutOfMemory);
errdefer castle_media_stbi_free(buffer_ptr_f16);
for (buffer_ptr_f16[0 .. ux * uy], buffer_ptr_f32[0 .. ux * uy]) |*sample_f16, sample_f32| {
sample_f16.* = .init(
std.math.clamp(@as(f16, @floatCast(sample_f32.x)), -std.math.floatMax(f16), std.math.floatMax(f16)),
std.math.clamp(@as(f16, @floatCast(sample_f32.y)), -std.math.floatMax(f16), std.math.floatMax(f16)),
std.math.clamp(@as(f16, @floatCast(sample_f32.z)), -std.math.floatMax(f16), std.math.floatMax(f16)),
std.math.clamp(@as(f16, @floatCast(sample_f32.w)), -std.math.floatMax(f16), std.math.floatMax(f16)),
);
}
return .initBuffer(ux, uy, buffer_ptr_f16[0 .. ux * uy]);
}
/// On success, must free memory by calling `freeHdr` method.
pub fn loadHdrIo(self: *Self, reader: *std.io.Reader) !image.Hdr {
current_self = self;
defer current_self = undefined;
var x: i32 = undefined;
var y: i32 = undefined;
const res_f32 = import.stbi_loadf_from_callbacks(&.std_io_reader_interface, reader, &x, &y, null, 4) orelse return error.StbiError;
defer castle_media_stbi_free(res_f32);
const ux: u32 = @intCast(x);
const uy: u32 = @intCast(y);
const buffer_ptr_f32: [*]vm.Vector4 = @ptrCast(res_f32);
const buffer_ptr_f16: [*]vm.ColorHdr = @ptrCast(castle_media_stbi_malloc(ux * uy * @sizeOf(vm.ColorHdr)) orelse return error.OutOfMemory);
errdefer castle_media_stbi_free(buffer_ptr_f16);
for (buffer_ptr_f16[0 .. ux * uy], buffer_ptr_f32[0 .. ux * uy]) |*sample_f16, sample_f32| {
sample_f16.* = .init(
std.math.clamp(@as(f16, @floatCast(sample_f32.x)), -std.math.floatMax(f16), std.math.floatMax(f16)),
std.math.clamp(@as(f16, @floatCast(sample_f32.y)), -std.math.floatMax(f16), std.math.floatMax(f16)),
std.math.clamp(@as(f16, @floatCast(sample_f32.z)), -std.math.floatMax(f16), std.math.floatMax(f16)),
std.math.clamp(@as(f16, @floatCast(sample_f32.w)), -std.math.floatMax(f16), std.math.floatMax(f16)),
);
}
return .initBuffer(ux, uy, buffer_ptr_f16[0 .. ux * uy]);
}
pub fn freeDynamic(self: *Self, img: image.Dynamic) void {
current_self = self;
defer current_self = undefined;
castle_media_stbi_free(@ptrCast(@alignCast(img.data)));
}
pub fn freeHdr(self: *Self, img: image.Hdr) void {
current_self = self;
defer current_self = undefined;
castle_media_stbi_free(@ptrCast(@alignCast(img.data)));
}
// --- IO INTERFACE ------------------------------------------------------------
pub const IoCallbacks = extern struct {
/// Fill `data` with `size` bytes. Return number of bytes actually read.
read: ?*const fn (ctx: ?*anyopaque, data: [*]u8, size: i32) callconv(.c) i32,
/// Skip the next `n` bytes, or backtrack `-n` bytes if `n < 0`.
skip: ?*const fn (ctx: ?*anyopaque, n: i32) callconv(.c) i32,
/// Return non-zero value if at the end of file/data.
eof: ?*const fn (cxt: ?*anyopaque) callconv(.c) i32,
pub const std_io_reader_interface: IoCallbacks = .{
.read = stdIoReader_ReadFn,
.skip = stdIoReader_SkipFn,
.eof = stdIoReader_EofFn,
};
pub fn stdIoReader_ReadFn(ctx: ?*anyopaque, data: [*]u8, size: i32) callconv(.c) i32 {
const reader: *std.Io.Reader = @ptrCast(@alignCast(ctx.?));
const bytes_read = reader.readSliceShort(data[0..@intCast(size)]) catch return 0;
return @intCast(bytes_read);
}
pub fn stdIoReader_SkipFn(ctx: ?*anyopaque, n: i32) callconv(.c) i32 {
const reader: *std.Io.Reader = @ptrCast(@alignCast(ctx.?));
// NOTE stb_image.h actually discards the return value from this
// callback. If an actual error occurs, we're cooked (but it will be
// very likely caught as a parsing error later).
_ = reader.discardAll(@intCast(n)) catch return 0;
return 0;
}
pub fn stdIoReader_EofFn(ctx: ?*anyopaque) callconv(.c) i32 {
const reader: *std.Io.Reader = @ptrCast(@alignCast(ctx.?));
_ = reader.peekByte() catch return 1;
return 0;
}
};
// --- MALLOC INTERFACE --------------------------------------------------------
threadlocal var current_self: *Self = undefined;
export fn castle_media_stbi_malloc(size: usize) callconv(.c) VoidPtr {
const self = current_self;
self.mutex.lock();
defer self.mutex.unlock();
self.allocations.ensureUnusedCapacity(self.allocator, 1) catch return null;
const memory = self.allocator.alignedAlloc(u8, alignment, size) catch return null;
self.allocations.putAssumeCapacityNoClobber(memory.ptr, size);
self.allocated_bytes += size;
//log.debug("Allocated {d} bytes(s) at 0x{x}", .{ size, @intFromPtr(memory.ptr) });
return memory.ptr;
}
export fn castle_media_stbi_realloc(maybe_ptr: VoidPtr, size: usize) callconv(.c) VoidPtr {
const self = current_self;
self.mutex.lock();
defer self.mutex.unlock();
// NOTE If we were pedantic, we would consider the fact that we might not
// need unused capacity if the memory doesn't get relocated.
self.allocations.ensureUnusedCapacity(self.allocator, 1) catch return null;
const old_memory = if (maybe_ptr) |ptr| blk_then: {
const old_size = self.allocations.get(ptr).?;
break :blk_then @as([*]align(alignment.toByteUnits()) u8, @ptrCast(ptr))[0..old_size];
} else blk_else: {
break :blk_else @as([]align(alignment.toByteUnits()) u8, &.{});
};
const memory = self.allocator.realloc(old_memory, size) catch return null;
if (maybe_ptr) |ptr| {
const old_size = self.allocations.fetchRemove(ptr).?.value;
self.allocated_bytes -= old_size;
}
self.allocations.putAssumeCapacityNoClobber(memory.ptr, size);
self.allocated_bytes += size;
//log.debug("Reallocated into {d} bytes(s) at 0x{x} from 0x{x}", .{ size, @intFromPtr(memory.ptr), @intFromPtr(maybe_ptr) });
return memory.ptr;
}
export fn castle_media_stbi_free(maybe_ptr: VoidPtr) callconv(.c) void {
const self = current_self;
if (maybe_ptr) |ptr| {
self.mutex.lock();
defer self.mutex.unlock();
const size = self.allocations.fetchRemove(ptr).?.value;
self.allocated_bytes -= size;
const memory = @as([*]align(alignment.toByteUnits()) u8, @ptrCast(ptr))[0..size];
self.allocator.free(memory);
}
}

View File

@@ -0,0 +1,17 @@
#include <stddef.h>
void *castle_media_stbi_malloc(size_t size);
void *castle_media_stbi_realloc(void *ptr, size_t size);
void castle_media_stbi_free(void *ptr);
#define STBI_MALLOC(size) castle_media_stbi_malloc(size)
#define STBI_REALLOC(ptr, size) castle_media_stbi_realloc(ptr, size)
#define STBI_FREE(ptr) castle_media_stbi_free(ptr)
#define STBI_NO_STDIO
#define STBI_ONLY_JPEG
#define STBI_ONLY_PNG
#define STBI_ONLY_HDR
#define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h"

File diff suppressed because it is too large Load Diff

View File

@@ -1,40 +0,0 @@
const std = @import("std");
pub fn build(b: *std.Build) void {
const target = b.resolveTargetQuery(.{
.cpu_arch = .x86_64,
.os_tag = .windows,
.abi = .msvc,
});
const optimize = b.standardOptimizeOption(.{});
const zigwin32 = b.dependency("zigwin32", .{});
const zigwin32_mod = zigwin32.module("win32");
zigwin32_mod.resolved_target = target;
zigwin32_mod.optimize = optimize;
const exe_mod = b.createModule(.{
.root_source_file = b.path("src/main.zig"),
.target = target,
.optimize = optimize,
});
exe_mod.addImport("win32", zigwin32_mod);
const exe = b.addExecutable(.{
.name = "sciter",
.root_module = exe_mod,
});
b.installArtifact(exe);
b.getInstallStep().dependOn(&b.addInstallBinFile(b.path("vendor/sciter.dll"), "sciter.dll").step);
const run_cmd = b.addRunArtifact(exe);
run_cmd.step.dependOn(b.getInstallStep());
if (b.args) |args| {
run_cmd.addArgs(args);
}
const run_step = b.step("run", "Run the app");
run_step.dependOn(&run_cmd.step);
}

View File

@@ -1,18 +0,0 @@
.{
.name = .sciter,
.version = "0.0.0",
.minimum_zig_version = "0.15.2",
.paths = .{
"src",
"vendor",
"build.zig",
"build.zig.zon",
},
.fingerprint = 0x51b124a630f074d7,
.dependencies = .{
.zigwin32 = .{
.url = "https://github.com/marlersoft/zigwin32/archive/5587b16fa040573846a6bf531301f6206d31a6bf.zip",
.hash = "zigwin32-25.0.28-preview-AAAAAICM5AMResOGQnQ85mfe60TTOQeMtt7GRATUOKoP",
},
},
}

View File

@@ -1,39 +0,0 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Sciter Demo</title>
<style>
html {
margin: 0;
padding: 0;
width: 100%;
height: 100%;
}
body {
font-size: 16px;
font-family: Arial, Helvetica, sans-serif;
background-color: white;
color: black;
}
#hello {
width: 120px;
height: 32px;
margin: 1*;
padding: 0;
border: 1px solid black;
text-align: center;
line-height: 32px;
}
</style>
</head>
<body>
<p id="hello">Hello, World!</p>
</body>
</html>

View File

@@ -1,115 +0,0 @@
const sciter = @import("sciter.zig");
const std = @import("std");
const wam = @import("win32").ui.windows_and_messaging;
const win32 = @import("win32").foundation;
const L = std.unicode.utf8ToUtf16LeStringLiteral;
const WINAPI = @import("std").builtin.CallingConvention.winapi;
const html = @embedFile("index.html");
const min_track_size: win32.POINT = .{ .x = 640, .y = 480 };
var sciter_api: *sciter.API = undefined;
pub fn wWinMain(
hInstance: std.os.windows.HINSTANCE,
hPrevInstance: ?std.os.windows.HINSTANCE,
lpCmdLine: [*:0]u16,
nCmdShow: i32,
) i32 {
_ = hPrevInstance;
_ = lpCmdLine;
const sciter_module = std.os.windows.LoadLibraryW(L("sciter.dll")) catch |err| {
_ = wam.MessageBoxW(
null,
switch (err) {
error.FileNotFound => L("Couldn't find sciter.dll."),
else => L("An unknown error occured while trying to load sciter.dll."),
},
L("Critical error"),
wam.MB_ICONHAND,
);
std.posix.exit(1);
};
const sciter_api_fn: *const fn () ?*sciter.API = @ptrCast(std.os.windows.kernel32.GetProcAddress(sciter_module, "SciterAPI") orelse {
_ = wam.MessageBoxW(null, L("Couldn't load Sciter API."), L("Critical error"), wam.MB_ICONHAND);
std.posix.exit(1);
});
sciter_api = sciter_api_fn() orelse {
_ = wam.MessageBoxW(null, L("Couldn't load Sciter API."), L("Critical error"), wam.MB_ICONHAND);
std.posix.exit(1);
};
std.debug.print("Sciter API version is: 0x{X:0>8}\n", .{sciter_api.version});
const wc = std.mem.zeroInit(wam.WNDCLASSEXW, .{
.cbSize = @sizeOf(wam.WNDCLASSEXW),
.lpfnWndProc = &WindowProc,
.hInstance = hInstance,
.hIcon = wam.LoadIconW(null, wam.IDI_APPLICATION),
.hCursor = wam.LoadCursorW(null, wam.IDC_ARROW),
.lpszClassName = L("SCITER_WINDOW"),
});
const atom = wam.RegisterClassExW(&wc);
std.debug.assert(atom != 0);
const hwnd = wam.CreateWindowExW(
wam.WS_EX_APPWINDOW,
wc.lpszClassName,
L("Sciter Demo"),
wam.WS_OVERLAPPEDWINDOW,
-1,
-1,
-1,
-1,
null,
null,
wc.hInstance,
null,
) orelse {
_ = wam.MessageBoxW(null, L("Couldn't create window."), L("Critical error"), wam.MB_ICONHAND);
std.posix.exit(1);
};
_ = sciter_api.SciterLoadHtml(hwnd, html, html.len, L("/"));
_ = wam.ShowWindow(hwnd, @bitCast(nCmdShow));
var msg = std.mem.zeroes(wam.MSG);
while (wam.GetMessageW(&msg, null, 0, 0) > 0) {
_ = wam.TranslateMessage(&msg);
_ = wam.DispatchMessageW(&msg);
}
return @bitCast(@as(u32, @truncate(msg.wParam)));
}
fn WindowProc(
hwnd: win32.HWND,
uMsg: u32,
wParam: win32.WPARAM,
lParam: win32.LPARAM,
) callconv(WINAPI) win32.LRESULT {
switch (uMsg) {
wam.WM_DESTROY => {
wam.PostQuitMessage(0);
},
wam.WM_GETMINMAXINFO => {
const mmi: *wam.MINMAXINFO = @ptrFromInt(@as(usize, @bitCast(lParam)));
mmi.ptMinTrackSize = min_track_size;
},
else => {},
}
var handled: sciter.Bool = .FALSE;
const result = sciter_api.SciterProcND(hwnd, uMsg, wParam, lParam, &handled);
if (handled != .FALSE) {
return result;
}
return wam.DefWindowProcW(hwnd, uMsg, wParam, lParam);
}

View File

@@ -1,641 +0,0 @@
pub const CStr = [*:0]const u8;
pub const CWStr = [*:0]const u16;
pub const Element = opaque {};
pub const HElement = *Element;
pub const Node = opaque {};
pub const HNode = *Node;
pub const SArchive = opaque {};
pub const HSArchive = *SArchive;
/// - Windows: `HWND`
/// - OS X: `NSView*`
/// - Linux/GTK: `GtkWidget*`
pub const HWindow = *anyopaque;
pub const BehaviorEventParams = extern struct {
cmd: BehaviorEvents,
he_target: ?HElement,
he: ?HElement,
reason: extern union {
click: enum(usize) { BY_MOUSE_CLICK, BY_KEY_CLICK, SYNTHESIZED, BY_MOUSE_ON_ICON },
edit_changed: enum(usize) { BY_INS_CHAR, BY_INS_CHARS, BY_DEL_CHAR, BY_DEL_CHARS, BY_UNDO_REDO },
custom: usize,
},
data: Value,
name: ?CWStr,
};
const BehaviorEvents = enum(u32) {
BUTTON_CLICK = 0,
BUTTON_PRESS = 1,
VALUE_CHANGED = 2,
VALUE_CHANGING = 3,
SELECTION_CHANGED = 5,
SELECTION_CHANGING = 0xC,
POPUP_REQUEST = 7,
POPUP_READY = 8,
POPUP_DISMISSED = 9,
MENU_ITEM_ACTIVE = 0xA,
MENU_ITEM_CLICK = 0xB,
CONTEXT_MENU_REQUEST = 0x10,
VISUAL_STATUS_CHANGED = 0x11,
DISABLED_STATUS_CHANGED = 0x12,
POPUP_DISMISSING = 0x13,
CONTENT_CHANGED = 0x15,
HYPERLINK_CLICK = 0x80,
ELEMENT_COLLAPSED = 0x90,
ELEMENT_EXPANDED = 0x91,
ACTIVATE_CHILD = 0x92,
FORM_SUBMIT = 0x96,
FORM_RESET = 0x97,
DOCUMENT_COMPLETE = 0x98,
HISTORY_PUSH = 0x99,
HISTORY_DROP = 0x9A,
HISTORY_PRIOR = 0x9B,
HISTORY_NEXT = 0x9C,
HISTORY_STATE_CHANGED = 0x9D,
CLOSE_POPUP = 0x9E,
REQUEST_TOOLTIP = 0x9F,
ANIMATION = 0xA0,
TRANSITION = 0xA1,
SWIPE = 0xB0,
DOCUMENT_CREATED = 0xC0,
DOCUMENT_CLOSE_REQUEST = 0xC1,
DOCUMENT_CLOSE = 0xC2,
DOCUMENT_READY = 0xC3,
DOCUMENT_PARSED = 0xC4,
//DOCUMENT_RELOAD = 0xC5,
DOCUMENT_CLOSING = 0xC6,
CONTAINER_CLOSE_REQUEST = 0xC7,
CONTAINER_CLOSING = 0xC8,
VIDEO_INITIALIZED = 0xD1,
VIDEO_STARTED = 0xD2,
VIDEO_STOPPED = 0xD3,
VIDEO_BIND_RQ = 0xD4,
VIDEO_FRAME_REQUEST = 0xD8,
PAGINATION_STARTS = 0xE0,
PAGINATION_PAGE = 0xE1,
PAGINATION_ENDS = 0xE2,
CUSTOM = 0xF0,
EGL_RENDER = 0x20,
/// All custom event codes shall be greater than this number. All codes
/// below this will be used solely by application - Sciter will not
/// intrepret it and will do just dispatching. To send event notifications
/// with these codes use SciterSend/PostEvent API.
FIRST_APPLICATION_EVENT_CODE = 0x100,
_,
};
pub const Bool = enum(u32) {
FALSE = 0,
TRUE = 1,
_,
};
pub const DomResult = enum(i32) {
OK = 0,
INVALID_HWND = 1,
INVALID_HANDLE = 2,
PASSIVE_HANDLE = 3,
INVALID_PARAMETER = 4,
OPERATION_FAILED = 5,
OK_NOT_HANDLED = -1,
};
pub const CtlType = enum(u32) {
NO = 0,
UNKNOWN = 1,
EDIT = 2,
NUMERIC = 3,
CLICKABLE = 4,
BUTTON = 5,
CHECKBOX = 6,
RADIO = 7,
SELECT_SINGLE = 8,
SELECT_MULTIPLE = 9,
DD_SELECT = 10,
TEXTAREA = 11,
HTMLAREA = 12,
PASSWORD = 13,
PROGRESS = 14,
SLIDER = 15,
DECIMAL = 16,
CURRENCY = 17,
SCROLLBAR = 18,
LIST = 19,
RICHTEXT = 20,
CALENDAR = 21,
DATE = 22,
TIME = 23,
FILE = 24,
PATH = 25,
HYPERLINK = 26,
FORM = 27,
MENUBAR = 28,
MENU = 29,
MENUBUTTON = 30,
FRAME = 31,
FRAMESET = 32,
TOOLTIP = 33,
HIDDEN = 34,
URL = 35,
TOOLBAR = 36,
WINDOW = 37,
LABEL = 38,
IMAGE = 39,
PLAINTEXT = 40,
SELECT_TREE = 41,
};
pub const ElementAreas = packed struct {
relative: enum(u4) {
ROOT = 0x1,
SELF = 0x2,
CONTAINER = 0x3,
VIEW = 0x4,
_,
} = @enumFromInt(0x0),
area: enum(u4) {
CONTENT_BOX = 0x0,
PADDING_BOX = 0x1,
BORDER_BOX = 0x2,
MARGIN_BOX = 0x3,
BACK_IMAGE_AREA = 0x4,
FORE_IMAGE_AREA = 0x5,
SCROLLABLE_AREA = 0x6,
_,
} = .CONTENT_BOX,
_pad8: u8 = 0,
as_ppx: bool = false,
_pad17: u15 = 0,
};
pub const MethodParams = extern struct {
method_id: u32,
};
pub const NodeType = enum(u32) { ELEMENT, TEXT, COMMENT };
pub const NodeInsTarget = enum(u32) { BEFORE, AFTER, APPEND, PREPEND };
pub const Point = extern struct {
x: i32,
y: i32,
};
pub const Rect = extern struct {
left: i32,
top: i32,
right: i32,
bottom: u32,
};
pub const RequestParam = extern struct {
name: CWStr,
value: CWStr,
};
pub const RequestType = enum(u32) {
GET_ASYNC,
POST_ASYNC,
GET_SYNC,
POST_SYNC,
};
pub const ResourceType = enum(u32) {
HTML = 0,
IMAGE = 1,
STYLE = 2,
CURSOR = 3,
SCRIPT = 4,
RAW = 5,
FONT,
SOUND,
FORCE_DWORD = 0xFFFFFFFF,
};
pub const SetElementHtml = enum(u32) {
SIH_REPLACE_CONTENT = 0,
SIH_INSERT_AT_START = 1,
SIH_APPEND_AFTER_LAST = 2,
SOH_REPLACE = 3,
SOH_INSERT_BEFORE = 4,
SOH_INSERT_AFTER = 5,
};
pub const Size = extern struct {
cx: i32,
cy: i32,
};
pub const Value = extern struct {
type: ValueType,
unit: extern union {
value: ValueUnit,
/// For when `Value.type == .OBJECT`
object: ValueUnitObject,
},
data: u64,
};
pub const ValueType = enum(u32) {
UNDEFINED = 0,
NULL = 1,
BOOL = 2,
i32 = 3,
FLOAT = 4,
STRING = 5,
DATE = 6,
BIG_INT = 7,
LENGTH = 8,
ARRAY = 9,
MAP = 10,
FUNCTION = 11,
BYTES = 12,
OBJECT = 13,
RESOURCE = 15,
DURATION = 17,
ANGLE = 18,
COLOR = 19,
ASSET = 21,
};
pub const ValueUnit = enum(u32) {
EM = 1,
EX = 2,
PR = 3,
SP = 4,
PX = 7,
IN = 8,
CM = 9,
MM = 10,
PT = 11,
PC = 12,
DIP = 13,
PR_WIDTH = 16,
PR_HEIGHT = 17,
PR_VIEW_WIDTH = 18,
PR_VIEW_HEIGHT = 19,
PR_VIEW_MIN = 20,
PR_VIEW_MAX = 21,
REM = 22,
PPX = 23,
CH = 24,
};
pub const ValueUnitObject = enum(u32) {
ARRAY = 0,
OBJECT = 1,
CLASS = 2,
NATIVE = 3,
FUNCTION = 4,
ERROR = 5,
BUFFER = 6,
};
pub const ValueResult = enum(i32) {
OK_TRUE = -1,
OK = 0,
BAD_PARAMETER = 1,
INCOMPATIBLE_TYPE = 2,
};
pub const OutputSubsystems = enum(u32) {
DOM = 0,
CSSS,
CSS,
TIS,
};
pub const CallbackNotification = extern struct {
code: u32,
hwnd: HWindow,
};
pub const XMsgCode = enum(u32) {
CREATE = 0,
DESTROY = 1,
SIZE = 2,
PAINT = 3,
RESOLUTION = 4,
HEARTBIT = 5,
MOUSE = 6,
KEY = 7,
FOCUS = 8,
};
pub const XMsg = extern struct {
msg: XMsgCode,
};
pub const OmAsset = extern struct {
isa: *OmAssetClass,
};
pub const OmAssetClass = extern struct {
asset_add_ref: *const fn (thing: *OmAsset) callconv(.c) c_long,
asset_release: *const fn (thing: *OmAsset) callconv(.c) c_long,
asset_get_interface: *const fn (thing: *OmAsset, name: CStr, out: *?*anyopaque) callconv(.c) c_long,
asset_get_passport: *const fn (thins: *OmAsset) callconv(.c) ?*OmPassport,
};
pub const OmPassport = opaque {};
pub const ValueStringCvtType = enum(u32) {
CVT_SIMPLE,
CVT_JSON_LITERAL,
CVT_JSON_MAP,
CVT_XJSON_LITERAL,
};
const ByteReceiver = fn (str: [*]const u8, num_bytes: u32, param: ?*anyopaque) callconv(.c) void;
const DebugOutputProc = fn (param: ?*anyopaque, subsystem: OutputSubsystems, severity: u32, text: [*]const u16, text_length: u32) callconv(.c) void;
const ElementCallback = fn (he: HElement, param: ?*anyopaque) callconv(.c) Bool;
const ElementComparator = fn (he1: HElement, he2: HElement, param: ?*anyopaque) callconv(.c) i32;
const ElementEventProc = fn (tag: ?*anyopaque, he: HElement, evtg: u32, prms: ?*anyopaque) callconv(.c) Bool;
const HostCallback = fn (pns: *CallbackNotification, callback_param: ?*anyopaque) callconv(.c) u32;
const KeyValueCallback = fn (param: ?*anyopaque, pkey: *const Value, pval: *const Value) callconv(.c) Bool;
const NativeFunctorInvoke = fn (tag: ?*anyopaque, argc: u32, argv: [*]const Value, retval: *Value) callconv(.c) void;
const NativeFunctorRelease = fn (tag: ?*anyopaque) callconv(.c) void;
const StrReceiver = fn (str: [*]const u8, num_bytes: u32, param: ?*anyopaque) callconv(.c) void;
const WindowDelegate = fn (hwnd: HWindow, msg: u32, wParam: usize, lParam: isize, pbHandled: *Bool) callconv(.c) isize;
const WStrReceiver = fn (str: [*]const u16, num_bytes: u32, param: ?*anyopaque) callconv(.c) void;
pub const API = extern struct {
version: u32,
SciterClassName: *const fn () callconv(.c) CWStr,
SciterVersion: *const fn (n: u32) callconv(.c) u32,
SciterDataReady: *const fn (hwnd: HWindow, uri: CWStr, data: [*]const u8, dataLength: u32) callconv(.c) Bool,
SciterDataReadyAsync: *const fn (hwnd: HWindow, uri: CWStr, data: [*]const u8, dataLength: u32, requestId: ?*anyopaque) callconv(.c) Bool,
SciterProc: *const fn (hwnd: HWindow, msg: u32, wParam: usize, lParam: isize) callconv(.c) isize,
SciterProcND: *const fn (hwnd: HWindow, msg: u32, wParam: usize, lParam: isize, pbHandled: *Bool) callconv(.c) isize,
SciterLoadFile: *const fn (hwnd: HWindow, filename: CWStr) callconv(.c) Bool,
SciterLoadHtml: *const fn (hwnd: HWindow, html: [*]const u8, htmlSize: u32, baseUrl: CWStr) callconv(.c) Bool,
SciterSetCallback: *const fn (hwnd: HWindow, cb: ?*HostCallback, cbParam: ?*anyopaque) callconv(.c) void,
SciterSetMasterCSS: *const fn (utf8: [*]const u8, numBytes: u32) callconv(.c) Bool,
SciterAppendMasterCSS: *const fn (utf8: [*]const u8, numBytes: u32) callconv(.c) Bool,
SciterSetCSS: *const fn (hwnd: HWindow, utf8: [*]const u8, numBytes: u32, baseUrl: CWStr, mediaType: CWStr) callconv(.c) Bool,
SciterSetMediaType: *const fn (hwnd: HWindow, mediaType: CWStr) callconv(.c) Bool,
SciterSetMediaVars: *const fn (hwnd: HWindow, mediaVars: *const Value) callconv(.c) Bool,
SciterGetMinWidth: *const fn (hwnd: HWindow) callconv(.c) u32,
SciterGetMinHeight: *const fn (hwnd: HWindow, width: u32) callconv(.c) u32,
SciterCall: *const fn (hWnd: HWindow, functionName: CStr, argc: u32, argv: [*]const Value, retval: *Value) callconv(.c) Bool,
SciterEval: *const fn (hwnd: HWindow, script: [*]const u16, scriptLength: u32, pretval: *Value) callconv(.c) Bool,
SciterUpdateWindow: *const fn (hwnd: HWindow) callconv(.c) void,
/// Win32 MSG
SciterTranslateMessage: *const fn (lpMsg: *anyopaque) callconv(.c) Bool,
SciterSetOption: *const fn (hWnd: HWindow, option: u32, value: usize) callconv(.c) Bool,
SciterGetPPI: *const fn (hwnd: HWindow, px: *u32, py: *u32) callconv(.c) void,
SciterGetViewExpando: *const fn (hwnd: HWindow, pval: *Value) callconv(.c) Bool,
/// ID2D1RenderTarget
SciterRenderD2D: *const fn (hwnd: HWindow, prt: *anyopaque) callconv(.c) Bool,
/// ID2D1Factory
SciterD2DFactory: *const fn (ppf: *anyopaque) callconv(.c) Bool,
/// IDWriteFactory
SciterDWFactory: *const fn (ppf: *anyopaque) callconv(.c) Bool,
SciterGraphicsCaps: *const fn (pcaps: *u32) callconv(.c) Bool,
SciterSetHomeURL: *const fn (hwnd: HWindow, baseUrl: CWStr) callconv(.c) Bool,
SciterCreateNSView: *anyopaque,
SciterCreateWidget: *anyopaque,
SciterCreateWindow: *const fn (creationFlags: u32, frame: *Rect, delegate: *const WindowDelegate, delegateParam: ?*anyopaque, parent: HWindow) callconv(.c) HWindow,
SciterSetupDebugOutput: *const fn (hwndOrNull: ?HWindow, param: ?*anyopaque, pfOutput: ?*DebugOutputProc) callconv(.c) void,
// --- DOM API -------------------------------------------------------------
Sciter_UseElement: *const fn (he: HElement) callconv(.c) DomResult,
Sciter_UnuseElement: *const fn (he: HElement) callconv(.c) DomResult,
SciterGetRootElement: *const fn (hwnd: HWindow, phe: *HElement) callconv(.c) DomResult,
SciterGetFocusElement: *const fn (hwnd: HWindow, phe: *HElement) callconv(.c) DomResult,
SciterFindElement: *const fn (hwnd: HWindow, pt: Point, phe: *HElement) callconv(.c) DomResult,
SciterGetChildrenCount: *const fn (he: HElement, count: *u32) callconv(.c) DomResult,
SciterGetNthChild: *const fn (he: HElement, n: u32, phe: *HElement) callconv(.c) DomResult,
SciterGetParentElement: *const fn (he: HElement, p_parent_he: *HElement) callconv(.c) DomResult,
SciterGetElementHtmlCB: *const fn (he: HElement, outer: Bool, rcv: *const ByteReceiver, rcv_param: ?*anyopaque) callconv(.c) DomResult,
SciterGetElementTextCB: *const fn (he: HElement, rcv: *const WStrReceiver, rcv_param: ?*anyopaque) callconv(.c) DomResult,
SciterSetElementText: *const fn (he: HElement, utf16: [*]const u16, length: u32) callconv(.c) DomResult,
SciterGetAttributeCount: *const fn (he: HElement, p_count: *u32) callconv(.c) DomResult,
SciterGetNthAttributeNameCB: *const fn (he: HElement, n: u32, rcv: *const StrReceiver, rcv_param: ?*anyopaque) callconv(.c) DomResult,
SciterGetNthAttributeValueCB: *const fn (he: HElement, n: u32, rcv: *const WStrReceiver, rcv_param: ?*anyopaque) callconv(.c) DomResult,
SciterGetAttributeByNameCB: *const fn (he: HElement, name: CStr, rcv: *const WStrReceiver, rcv_param: ?*anyopaque) callconv(.c) DomResult,
SciterSetAttributeByName: *const fn (he: HElement, name: CStr, value: CWStr) callconv(.c) DomResult,
SciterClearAttributes: *const fn (he: HElement) callconv(.c) DomResult,
SciterGetElementIndex: *const fn (he: HElement, p_index: *u32) callconv(.c) DomResult,
SciterGetElementType: *const fn (he: HElement, p_type: *CStr) callconv(.c) DomResult,
SciterGetElementTypeCB: *const fn (he: HElement, rcv: *const StrReceiver, rcv_param: ?*anyopaque) callconv(.c) DomResult,
SciterGetStyleAttributeCB: *const fn (he: HElement, name: CStr, rcv: *const WStrReceiver, rcv_param: ?*anyopaque) callconv(.c) DomResult,
SciterSetStyleAttribute: *const fn (he: HElement, name: CStr, value: CWStr) callconv(.c) DomResult,
SciterGetElementLocation: *const fn (he: HElement, p_location: *Rect, areas: ElementAreas) callconv(.c) DomResult,
SciterScrollToView: *const fn (he: HElement, SciterScrollFlags: u32) callconv(.c) DomResult,
SciterUpdateElement: *const fn (he: HElement, andForceRender: Bool) callconv(.c) DomResult,
SciterRefreshElementArea: *const fn (he: HElement, rc: Rect) callconv(.c) DomResult,
SciterSetCapture: *const fn (he: HElement) callconv(.c) DomResult,
SciterReleaseCapture: *const fn (he: HElement) callconv(.c) DomResult,
SciterGetElementHwnd: *const fn (he: HElement, p_hwnd: *HWindow, rootWindow: Bool) callconv(.c) DomResult,
SciterCombineURL: *const fn (he: HElement, szUrlBuffer: [*]u16, UrlBufferSize: u32) callconv(.c) DomResult,
SciterSelectElements: *const fn (he: HElement, CSS_selectors: CStr, callback: *const ElementCallback, param: ?*anyopaque) callconv(.c) DomResult,
SciterSelectElementsW: *const fn (he: HElement, CSS_selectors: CWStr, callback: *const ElementCallback, param: ?*anyopaque) callconv(.c) DomResult,
SciterSelectParent: *const fn (he: HElement, selector: CStr, depth: u32, heFound: *HElement) callconv(.c) DomResult,
SciterSelectParentW: *const fn (he: HElement, selector: CWStr, depth: u32, heFound: *HElement) callconv(.c) DomResult,
SciterSetElementHtml: *const fn (he: HElement, html: [*]const u8, htmlLength: u32, where: SetElementHtml) callconv(.c) DomResult,
SciterGetElementUID: *const fn (he: HElement, puid: *u32) callconv(.c) DomResult,
SciterGetElementByUID: *const fn (hwnd: HWindow, uid: u32, phe: *HElement) callconv(.c) DomResult,
SciterShowPopup: *const fn (hePopup: HElement, heAnchor: HElement, placement: u32) callconv(.c) DomResult,
SciterShowPopupAt: *const fn (hePopup: HElement, pos: Point, placement: u32) callconv(.c) DomResult,
SciterHidePopup: *const fn (he: HElement) callconv(.c) DomResult,
SciterGetElementState: *const fn (he: HElement, pstateBits: *u32) callconv(.c) DomResult,
SciterSetElementState: *const fn (he: HElement, stateBitsToSet: u32, stateBitsToClear: u32, updateView: Bool) callconv(.c) DomResult,
SciterCreateElement: *const fn (tagname: CStr, textOrNull: ?CWStr, phe: *HElement) callconv(.c) DomResult,
SciterCloneElement: *const fn (he: HElement, phe: *HElement) callconv(.c) DomResult,
SciterInsertElement: *const fn (he: HElement, hparent: HElement, index: u32) callconv(.c) DomResult,
SciterDetachElement: *const fn (he: HElement) callconv(.c) DomResult,
SciterDeleteElement: *const fn (he: HElement) callconv(.c) DomResult,
SciterSetTimer: *const fn (he: HElement, milliseconds: u32, timer_id: usize) callconv(.c) DomResult,
SciterDetachEventHandler: *const fn (he: HElement, pep: *const ElementEventProc, tag: ?*anyopaque) callconv(.c) DomResult,
SciterAttachEventHandler: *const fn (he: HElement, pep: *const ElementEventProc, tag: ?*anyopaque) callconv(.c) DomResult,
SciterWindowAttachEventHandler: *const fn (hwndLayout: HWindow, pep: *const ElementEventProc, tag: ?*anyopaque, subscription: u32) callconv(.c) DomResult,
SciterWindowDetachEventHandler: *const fn (hwndLayout: HWindow, pep: *const ElementEventProc, tag: ?*anyopaque) callconv(.c) DomResult,
SciterSendEvent: *const fn (he: HElement, appEventCode: u32, heSource: HElement, reason: usize, handled: *Bool) callconv(.c) DomResult,
SciterPostEvent: *const fn (he: HElement, appEventCode: u32, heSource: HElement, reason: usize) callconv(.c) DomResult,
SciterCallBehaviorMethod: *const fn (he: HElement, params: *MethodParams) callconv(.c) DomResult,
SciterRequestElementData: *const fn (he: HElement, url: CWStr, dataType: u32, initiator: HElement) callconv(.c) DomResult,
SciterHttpRequest: *const fn (he: HElement, url: CWStr, dataType: ResourceType, requestType: RequestType, requestParams: [*]RequestParam, nParams: u32) callconv(.c) DomResult,
SciterGetScrollInfo: *const fn (he: HElement, scrollPos: *Point, viewRect: *Rect, contentSize: *Size) callconv(.c) DomResult,
SciterSetScrollPos: *const fn (he: HElement, scrollPos: Point, smooth: Bool) callconv(.c) DomResult,
SciterGetElementIntrinsicWidths: *const fn (he: HElement, pMinWidth: *i32, pMaxWidth: *i32) callconv(.c) DomResult,
SciterGetElementIntrinsicHeight: *const fn (he: HElement, forWidth: i32, pHeight: *i32) callconv(.c) DomResult,
SciterIsElementVisible: *const fn (he: HElement, pVisible: *Bool) callconv(.c) DomResult,
SciterIsElementEnabled: *const fn (he: HElement, pEnabled: *Bool) callconv(.c) DomResult,
SciterSortElements: *const fn (he: HElement, firstIndex: u32, lastIndex: u32, cmpFunc: *const ElementComparator, cmpFuncParam: ?*anyopaque) callconv(.c) DomResult,
SciterSwapElements: *const fn (he1: HElement, he2: HElement) callconv(.c) DomResult,
SciterTraverseUIEvent: *const fn (evt: u32, eventCtlStruct: ?*anyopaque, bOutProcessed: *Bool) callconv(.c) DomResult,
SciterCallScriptingMethod: *const fn (he: HElement, name: CStr, argv: [*]const Value, argc: u32, retval: *Value) callconv(.c) DomResult,
SciterCallScriptingFunction: *const fn (he: HElement, name: CStr, argv: [*]const Value, argc: u32, retval: *Value) callconv(.c) DomResult,
SciterEvalElementScript: *const fn (he: HElement, script: [*]const u16, scriptLength: u32, retval: *Value) callconv(.c) DomResult,
SciterAttachHwndToElement: *const fn (he: HElement, hwnd: HWindow) callconv(.c) DomResult,
SciterControlGetType: *const fn (he: HElement, pType: *CtlType) callconv(.c) DomResult,
SciterGetValue: *const fn (he: HElement, pval: *Value) callconv(.c) DomResult,
SciterSetValue: *const fn (he: HElement, pval: *const Value) callconv(.c) DomResult,
SciterGetExpando: *const fn (he: HElement, pval: *Value, forceCreation: Bool) callconv(.c) DomResult,
SciterGetObject: *const fn (he: HElement, pval: *void, forceCreation: Bool) callconv(.c) DomResult,
SciterGetElementNamespace: *const fn (he: HElement, pval: *void) callconv(.c) DomResult,
SciterGetHighlightedElement: *const fn (hwnd: HWindow, phe: *HElement) callconv(.c) DomResult,
SciterSetHighlightedElement: *const fn (hwnd: HWindow, he: HElement) callconv(.c) DomResult,
// --- DOM NODE API --------------------------------------------------------
SciterNodeAddRef: *const fn (hn: HNode) callconv(.c) DomResult,
SciterNodeRelease: *const fn (hn: HNode) callconv(.c) DomResult,
SciterNodeCastFromElement: *const fn (he: HElement, phn: *HNode) callconv(.c) DomResult,
SciterNodeCastToElement: *const fn (hn: HNode, he: *HElement) callconv(.c) DomResult,
SciterNodeFirstChild: *const fn (hn: HNode, phn: *HNode) callconv(.c) DomResult,
SciterNodeLastChild: *const fn (hn: HNode, phn: *HNode) callconv(.c) DomResult,
SciterNodeNextSibling: *const fn (hn: HNode, phn: *HNode) callconv(.c) DomResult,
SciterNodePrevSibling: *const fn (hn: HNode, phn: *HNode) callconv(.c) DomResult,
SciterNodeParent: *const fn (hnode: HNode, pheParent: *HElement) callconv(.c) DomResult,
SciterNodeNthChild: *const fn (hnode: HNode, n: u32, phn: *HNode) callconv(.c) DomResult,
SciterNodeChildrenCount: *const fn (hnode: HNode, pn: *u32) callconv(.c) DomResult,
SciterNodeType: *const fn (hnode: HNode, pNodeType: *NodeType) callconv(.c) DomResult,
SciterNodeGetText: *const fn (hnode: HNode, rcv: *const WStrReceiver, rcv_param: ?*anyopaque) callconv(.c) DomResult,
SciterNodeSetText: *const fn (hnode: HNode, text: [*]const u16, textLength: u32) callconv(.c) DomResult,
SciterNodeInsert: *const fn (hnode: HNode, where: NodeInsTarget, what: HNode) callconv(.c) DomResult,
SciterNodeRemove: *const fn (hnode: HNode, finalize: Bool) callconv(.c) DomResult,
SciterCreateTextNode: *const fn (text: [*]const u16, textLength: u32, phnode: *HNode) callconv(.c) DomResult,
SciterCreateCommentNode: *const fn (text: [*]const u16, textLength: u32, phnode: *HNode) callconv(.c) DomResult,
// --- Value API -----------------------------------------------------------
ValueInit: *const fn (pval: *Value) callconv(.c) u32,
ValueClear: *const fn (pval: *Value) callconv(.c) u32,
ValueCompare: *const fn (pval1: *const Value, pval2: *const Value) callconv(.c) u32,
ValueCopy: *const fn (pdst: *Value, psrc: *const Value) callconv(.c) u32,
ValueIsolate: *const fn (pdst: *Value) callconv(.c) u32,
ValueType: *const fn (pval: *const Value, pType: *u32, pUnits: *u32) callconv(.c) u32,
ValueStringData: *const fn (pval: *const Value, pChars: *[*]const u16, pNumChars: *u32) callconv(.c) u32,
ValueStringDataSet: *const fn (pval: *Value, chars: [*]const u16, numChars: u32, units: u32) callconv(.c) u32,
ValueIntData: *const fn (pval: *const Value, pData: *i32) callconv(.c) u32,
ValueIntDataSet: *const fn (pval: *Value, data: i32, type: u32, units: u32) callconv(.c) u32,
ValueInt64Data: *const fn (pval: *const Value, pData: *i64) callconv(.c) u32,
ValueInt64DataSet: *const fn (pval: *Value, data: i64, type: u32, units: u32) callconv(.c) u32,
ValueFloatData: *const fn (pval: *const Value, pData: *f64) callconv(.c) u32,
ValueFloatDataSet: *const fn (pval: *Value, data: f64, type: u32, units: u32) callconv(.c) u32,
ValueBinaryData: *const fn (pval: *const Value, pBytes: *[*]const u8, pnBytes: *u32) callconv(.c) u32,
ValueBinaryDataSet: *const fn (pval: *Value, pBytes: [*]const u8, nBytes: u32, type: u32, units: u32) callconv(.c) u32,
ValueElementsCount: *const fn (pval: *const Value, pn: *i32) callconv(.c) u32,
ValueNthElementValue: *const fn (pval: *const Value, n: i32, pretval: *Value) callconv(.c) u32,
ValueNthElementValueSet: *const fn (pval: *Value, n: i32, pval_to_set: *const Value) callconv(.c) u32,
ValueNthElementKey: *const fn (pval: *const Value, n: i32, pretval: *Value) callconv(.c) u32,
ValueEnumElements: *const fn (pval: *const Value, penum: *const KeyValueCallback, param: ?*anyopaque) callconv(.c) u32,
ValueSetValueToKey: *const fn (pval: *Value, pkey: *const Value, pval_to_set: *const Value) callconv(.c) u32,
ValueGetValueOfKey: *const fn (pval: *const Value, pkey: *const Value, pretval: *Value) callconv(.c) u32,
ValueToString: *const fn (pval: *Value, how: ValueStringCvtType) callconv(.c) u32,
ValueFromString: *const fn (pval: *Value, str: [*]const u16, strLength: u32, how: ValueStringCvtType) callconv(.c) u32,
ValueInvoke: *const fn (pval: *const Value, pthis: *Value, argc: u32, argv: *const Value, pretval: *Value, url: CWStr) callconv(.c) u32,
ValueNativeFunctorSet: *const fn (pval: *Value, pinvoke: *const NativeFunctorInvoke, prelease: *const NativeFunctorRelease, tag: *void) callconv(.c) u32,
ValueIsNativeFunctor: *const fn (pval: *const Value) callconv(.c) Bool,
// Used to be script VM API
reserved1: *anyopaque,
reserved2: *anyopaque,
reserved3: *anyopaque,
reserved4: *anyopaque,
SciterOpenArchive: *const fn (archiveData: [*]const u8, archiveDataLength: u32) callconv(.c) HSArchive,
SciterGetArchiveItem: *const fn (harc: HSArchive, path: CWStr, pdata: *[*]const u8, pdataLength: *u32) callconv(.c) Bool,
SciterCloseArchive: *const fn (harc: HSArchive) callconv(.c) Bool,
SciterFireEvent: *const fn (evt: *const BehaviorEventParams, post: Bool, handled: *Bool) callconv(.c) DomResult,
SciterGetCallbackParam: *const fn (hwnd: HWindow) callconv(.c) ?*anyopaque,
SciterPostCallback: *const fn (hwnd: HWindow, wparam: isize, lparam: usize, timeoutms: u32) callconv(.c) isize,
GetSciterGraphicsAPI: *const fn () callconv(.c) *GraphicsAPI,
GetSciterRequestAPI: *const fn () callconv(.c) *RequestAPI,
/// IDXGISwapChain
SciterCreateOnDirectXWindow: *const fn (hwnd: HWindow, pSwapChain: *anyopaque) callconv(.c) Bool,
SciterRenderOnDirectXWindow: *const fn (hwnd: HWindow, elementToRenderOrNull: HElement, frontLayer: Bool) callconv(.c) Bool,
/// IDXGISurface
SciterRenderOnDirectXTexture: *const fn (hwnd: HWindow, elementToRenderOrNull: HElement, surface: *anyopaque) callconv(.c) Bool,
SciterProcX: *const fn (hwnd: HWindow, pMsg: *XMsg) callconv(.c) Bool,
SciterAtomValue: *const fn (name: CStr) callconv(.c) u64,
SciterAtomNameCB: *const fn (atomv: u64, rcv: *const StrReceiver, rcv_param: ?*anyopaque) callconv(.c) Bool,
SciterSetGlobalAsset: *const fn (pass: *OmAsset) callconv(.c) Bool,
SciterGetElementAsset: *const fn (el: HElement, nameAtom: u64, ppass: *?*OmAsset) callconv(.c) DomResult,
SciterSetVariable: *const fn (hwndOrNull: HWindow, name: CStr, pvalToSet: *const Value) callconv(.c) u32,
SciterGetVariable: *const fn (hwndOrNull: HWindow, name: CStr, pvalToGet: *Value) callconv(.c) u32,
SciterElementUnwrap: *const fn (pval: *const Value, ppElement: *HElement) callconv(.c) u32,
SciterElementWrap: *const fn (pval: *Value, pElement: HElement) callconv(.c) u32,
SciterNodeUnwrap: *const fn (pval: *const Value, ppNode: *HNode) callconv(.c) u32,
SciterNodeWrap: *const fn (pval: *Value, pNode: HNode) callconv(.c) u32,
SciterReleaseGlobalAsset: *const fn (pass: *OmAsset) callconv(.c) Bool,
SciterExec: *const fn (appCmd: u32, p1: usize, p2: usize) callconv(.c) isize,
SciterWindowExec: *const fn (hwnd: HWindow, windowCmd: u32, p1: usize, p2: usize) callconv(.c) isize,
SciterEGLGetProcAddress: *const fn (procName: CStr) callconv(.c) ?*anyopaque,
SciterEGLSendEvent: *const fn (he: HElement, eventCode: u32, reason: usize) callconv(.c) DomResult,
SciterRequestAnimationFrameEvent: *const fn (he: HElement, eventCode: u32, reason: usize) callconv(.c) DomResult,
};
// TODO These APIs are actually well-known structs with function pointers in
// them, which may provide useful functionality
pub const GraphicsAPI = opaque {};
pub const RequestAPI = opaque {};

BIN
packages/sciter/vendor/sciter.dll (Stored with Git LFS) vendored

Binary file not shown.

View File

@@ -0,0 +1,30 @@
const std = @import("std");
const vm = @import("../root.zig");
pub const ColorHdr = extern struct {
r: f16,
g: f16,
b: f16,
a: f16,
pub const Array = [4]f16;
pub const zero = init(0, 0, 0, 0);
pub const one = init(1, 1, 1, 1);
pub inline fn init(r: f16, g: f16, b: f16, a: f16) ColorHdr {
return .{ .r = r, .g = g, .b = b, .a = a };
}
pub inline fn initArray(array: Array) ColorHdr {
return @bitCast(array);
}
pub inline fn asArray(self: ColorHdr) Array {
return @bitCast(self);
}
pub fn format(self: ColorHdr, w: *std.io.Writer) !void {
try w.print("ColorHdr[{d}, {d}, {d}, {d}]", .{ self.r, self.g, self.b, self.a });
}
};

View File

@@ -3,6 +3,7 @@ const std = @import("std");
// --- COLORS ------------------------------------------------------------------ // --- COLORS ------------------------------------------------------------------
pub const Color = @import("colors/Color.zig").Color; pub const Color = @import("colors/Color.zig").Color;
pub const ColorHdr = @import("colors/ColorHdr.zig").ColorHdr;
// --- MATRICES ---------------------------------------------------------------- // --- MATRICES ----------------------------------------------------------------

9
packages/x11/build.zig Normal file
View File

@@ -0,0 +1,9 @@
const std = @import("std");
pub fn build(b: *std.Build) void {
const mod = b.addModule("x11", .{
.root_source_file = b.path("src/root.zig"),
});
mod.linkSystemLibrary("X11", .{});
}

View File

@@ -0,0 +1,11 @@
.{
.name = .x11,
.version = "0.0.0",
.minimum_zig_version = "0.15.2",
.paths = .{
"src",
"build.zig",
"build.zig.zon",
},
.fingerprint = 0x3220e772d0ef0e80,
}

3182
packages/x11/src/root.zig Normal file

File diff suppressed because it is too large Load Diff