simd: add scalar fallbacks to all for build_options.simd false

pull/8840/head
Mitchell Hashimoto 2025-09-22 08:02:55 -07:00
parent 8aa4373aaf
commit 6893024c51
No known key found for this signature in database
GPG Key ID: 523D5DC389D273BC
7 changed files with 117 additions and 22 deletions

View File

@ -460,6 +460,7 @@ pub fn addOptions(self: *const Config, step: *std.Build.Step.Options) !void {
step.addOption(bool, "x11", self.x11);
step.addOption(bool, "wayland", self.wayland);
step.addOption(bool, "sentry", self.sentry);
step.addOption(bool, "simd", self.simd);
step.addOption(bool, "i18n", self.i18n);
step.addOption(ApprtRuntime, "app_runtime", self.app_runtime);
step.addOption(FontBackend, "font_backend", self.font_backend);

View File

@ -112,6 +112,7 @@ pub fn add(
const vt_options = @import("../terminal/build_options.zig");
vt_options.addOptions(b, step.root_module, .{
.artifact = .ghostty,
.simd = self.config.simd,
.slow_runtime_safety = switch (optimize) {
.Debug => true,
.ReleaseSafe,

View File

@ -1,4 +1,63 @@
const std = @import("std");
const options = @import("build_options");
const assert = std.debug.assert;
const log = std.log.scoped(.simd_base64);
// Used for the non-SIMD implementation
const Base64Decoder = std.base64.standard_no_pad.Decoder;
pub fn maxLen(input: []const u8) usize {
if (comptime options.simd) return ghostty_simd_base64_max_length(
input.ptr,
input.len,
);
return maxLenScalar(input);
}
fn maxLenScalar(input: []const u8) usize {
return Base64Decoder.calcSizeForSlice(scalarInput(input)) catch |err| {
log.warn("failed to calculate base64 size for payload: {}", .{err});
return 0;
};
}
pub fn decode(input: []const u8, output: []u8) error{Base64Invalid}![]const u8 {
if (comptime options.simd) {
const res = ghostty_simd_base64_decode(
input.ptr,
input.len,
output.ptr,
);
if (res < 0) return error.Base64Invalid;
return output[0..@intCast(res)];
}
return decodeScalar(input, output);
}
fn decodeScalar(
input_raw: []const u8,
output: []u8,
) error{Base64Invalid}![]const u8 {
const input = scalarInput(input_raw);
const size = maxLenScalar(input);
if (size == 0) return "";
assert(output.len >= size);
Base64Decoder.decode(
output,
scalarInput(input),
) catch return error.Base64Invalid;
return output[0..size];
}
/// For non-SIMD enabled builds, we trim the padding from the end of the
/// base64 input in order to get identical output with the SIMD version.
fn scalarInput(input: []const u8) []const u8 {
var i: usize = 0;
while (input[input.len - i - 1] == '=') i += 1;
return input[0 .. input.len - i];
}
// base64.cpp
extern "c" fn ghostty_simd_base64_max_length(
@ -11,16 +70,6 @@ extern "c" fn ghostty_simd_base64_decode(
output: [*]u8,
) isize;
pub fn maxLen(input: []const u8) usize {
return ghostty_simd_base64_max_length(input.ptr, input.len);
}
pub fn decode(input: []const u8, output: []u8) error{Base64Invalid}![]const u8 {
const res = ghostty_simd_base64_decode(input.ptr, input.len, output.ptr);
if (res < 0) return error.Base64Invalid;
return output[0..@intCast(res)];
}
test "base64 maxLen" {
const testing = std.testing;
const len = maxLen("aGVsbG8gd29ybGQ=");

View File

@ -1,11 +1,12 @@
const std = @import("std");
const options = @import("build_options");
// vt.cpp
extern "c" fn ghostty_simd_codepoint_width(u32) i8;
pub fn codepointWidth(cp: u32) i8 {
//return @import("ziglyph").display_width.codePointWidth(@intCast(cp), .half);
return ghostty_simd_codepoint_width(cp);
if (comptime options.simd) return ghostty_simd_codepoint_width(cp);
return @import("ziglyph").display_width.codePointWidth(@intCast(cp), .half);
}
test "codepointWidth basic" {

View File

@ -1,5 +1,6 @@
const std = @import("std");
const builtin = @import("builtin");
const options = @import("build_options");
extern "c" fn ghostty_simd_index_of(
needle: u8,
@ -8,8 +9,16 @@ extern "c" fn ghostty_simd_index_of(
) usize;
pub fn indexOf(input: []const u8, needle: u8) ?usize {
const result = ghostty_simd_index_of(needle, input.ptr, input.len);
return if (result == input.len) null else result;
if (comptime options.simd) {
const result = ghostty_simd_index_of(needle, input.ptr, input.len);
return if (result == input.len) null else result;
}
return indexOfScalar(input, needle);
}
fn indexOfScalar(input: []const u8, needle: u8) ?usize {
return std.mem.indexOfScalar(u8, input, needle);
}
test "indexOf" {

View File

@ -1,3 +1,6 @@
//! SIMD-optimized routines. If `build_options.simd` is false, then the API
//! still works but we fall back to pure Zig scalar implementations.
const std = @import("std");
const codepoint_width = @import("codepoint_width.zig");

View File

@ -1,4 +1,7 @@
const std = @import("std");
const options = @import("build_options");
const assert = std.debug.assert;
const indexOf = @import("index_of.zig").indexOf;
// vt.cpp
extern "c" fn ghostty_simd_decode_utf8_until_control_seq(
@ -17,15 +20,43 @@ pub fn utf8DecodeUntilControlSeq(
input: []const u8,
output: []u32,
) DecodeResult {
var decoded: usize = 0;
const consumed = ghostty_simd_decode_utf8_until_control_seq(
input.ptr,
input.len,
output.ptr,
&decoded,
);
assert(output.len >= input.len);
return .{ .consumed = consumed, .decoded = decoded };
if (comptime options.simd) {
var decoded: usize = 0;
const consumed = ghostty_simd_decode_utf8_until_control_seq(
input.ptr,
input.len,
output.ptr,
&decoded,
);
return .{ .consumed = consumed, .decoded = decoded };
}
return utf8DecodeUntilControlSeqScalar(input, output);
}
fn utf8DecodeUntilControlSeqScalar(
input: []const u8,
output: []u32,
) DecodeResult {
// Find our escape
const idx = indexOf(input, 0x1B) orelse input.len;
// Copy up to the escape
const view = std.unicode.Utf8View.init(input[0..idx]) catch unreachable;
var it = view.iterator();
var i: usize = 0;
while (it.nextCodepoint()) |cp| {
output[i] = @intCast(cp);
i += 1;
}
return .{
.consumed = idx,
.decoded = i,
};
}
test "decode no escape" {