mirror of
https://github.com/ghostty-org/ghostty.git
synced 2026-04-15 03:52:39 +00:00
Fixes #11177 Use per-search Oniguruma match params (retry_limit_in_search) in StringMap-backed link detection to avoid pathological backtracking hangs on very long lines. The units are ticks in the internal loop so its kind of opaque but this seems to still match some very long URLs. The test case in question was a 169K character line (which is now rejected).
153 lines
4.0 KiB
Zig
153 lines
4.0 KiB
Zig
const std = @import("std");
|
|
const c = @import("c.zig").c;
|
|
const types = @import("types.zig");
|
|
const errors = @import("errors.zig");
|
|
const testEnsureInit = @import("testing.zig").ensureInit;
|
|
const MatchParam = @import("match_param.zig").MatchParam;
|
|
const Region = @import("region.zig").Region;
|
|
const Error = errors.Error;
|
|
const ErrorInfo = errors.ErrorInfo;
|
|
const Encoding = types.Encoding;
|
|
const Option = types.Option;
|
|
const Syntax = types.Syntax;
|
|
|
|
pub const Regex = struct {
|
|
value: c.OnigRegex,
|
|
|
|
pub fn init(
|
|
pattern: []const u8,
|
|
options: Option,
|
|
enc: *Encoding,
|
|
syntax: *Syntax,
|
|
err: ?*ErrorInfo,
|
|
) !Regex {
|
|
var self: Regex = undefined;
|
|
_ = try errors.convertError(c.onig_new(
|
|
&self.value,
|
|
pattern.ptr,
|
|
pattern.ptr + pattern.len,
|
|
options.int(),
|
|
@ptrCast(@alignCast(enc)),
|
|
@ptrCast(@alignCast(syntax)),
|
|
@ptrCast(err),
|
|
));
|
|
return self;
|
|
}
|
|
|
|
pub fn deinit(self: *Regex) void {
|
|
c.onig_free(self.value);
|
|
}
|
|
|
|
/// Search an entire string for matches. This always returns a region
|
|
/// which may heap allocate (C allocator).
|
|
pub fn search(
|
|
self: *Regex,
|
|
str: []const u8,
|
|
options: Option,
|
|
) !Region {
|
|
return self.searchWithParam(str, options, null);
|
|
}
|
|
|
|
/// Search an entire string for matches. This always returns a region
|
|
/// which may heap allocate (C allocator).
|
|
pub fn searchWithParam(
|
|
self: *Regex,
|
|
str: []const u8,
|
|
options: Option,
|
|
match_param: ?*MatchParam,
|
|
) !Region {
|
|
var region: Region = .{};
|
|
|
|
// As part of the searchAdvanced API call below, onig may allocate
|
|
// memory even if we fail to match. We need to deinit if there are
|
|
// any errors to free that memory.
|
|
errdefer region.deinit();
|
|
|
|
_ = try self.searchAdvancedWithParam(
|
|
str,
|
|
0,
|
|
str.len,
|
|
®ion,
|
|
options,
|
|
match_param,
|
|
);
|
|
return region;
|
|
}
|
|
|
|
/// onig_search directly
|
|
pub fn searchAdvanced(
|
|
self: *Regex,
|
|
str: []const u8,
|
|
start: usize,
|
|
end: usize,
|
|
region: *Region,
|
|
options: Option,
|
|
) !usize {
|
|
return self.searchAdvancedWithParam(
|
|
str,
|
|
start,
|
|
end,
|
|
region,
|
|
options,
|
|
null,
|
|
);
|
|
}
|
|
|
|
/// onig_search_with_param directly
|
|
pub fn searchAdvancedWithParam(
|
|
self: *Regex,
|
|
str: []const u8,
|
|
start: usize,
|
|
end: usize,
|
|
region: *Region,
|
|
options: Option,
|
|
match_param: ?*MatchParam,
|
|
) !usize {
|
|
const pos = try errors.convertError(if (match_param) |param|
|
|
c.onig_search_with_param(
|
|
self.value,
|
|
str.ptr,
|
|
str.ptr + str.len,
|
|
str.ptr + start,
|
|
str.ptr + end,
|
|
@ptrCast(region),
|
|
options.int(),
|
|
param.value,
|
|
)
|
|
else
|
|
c.onig_search(
|
|
self.value,
|
|
str.ptr,
|
|
str.ptr + str.len,
|
|
str.ptr + start,
|
|
str.ptr + end,
|
|
@ptrCast(region),
|
|
options.int(),
|
|
));
|
|
|
|
return @intCast(pos);
|
|
}
|
|
};
|
|
|
|
test {
|
|
const testing = std.testing;
|
|
|
|
try testEnsureInit();
|
|
var re = try Regex.init("foo", .{}, Encoding.utf8, Syntax.default, null);
|
|
defer re.deinit();
|
|
|
|
var reg = try re.search("hello foo bar", .{});
|
|
defer reg.deinit();
|
|
try testing.expectEqual(@as(usize, 1), reg.count());
|
|
|
|
try testing.expectError(Error.Mismatch, re.search("hello", .{}));
|
|
|
|
var match_param = try MatchParam.init();
|
|
defer match_param.deinit();
|
|
try match_param.setRetryLimitInSearch(1000);
|
|
|
|
var reg_param = try re.searchWithParam("hello foo bar", .{}, &match_param);
|
|
defer reg_param.deinit();
|
|
try testing.expectEqual(@as(usize, 1), reg_param.count());
|
|
}
|