zlob 1.4.0

SIMD optimized glob pattern matching library faster than glob crate
const std = @import("std");
const mem = std.mem;

const glob = @import("zlob.zig");
const compiled_pattern = @import("compiled_pattern.zig");

const fnmatch_mod = glob.fnmatch;
const splitPathComponentsNormalized = compiled_pattern.splitPathComponentsNormalized;

/// Simple glob pattern matching with `**` support — no allocation required.
/// Lightweight alternative to `compiled_pattern.matchSinglePath` for cases
/// that don't need `ZLOB_PERIOD` handling or pre-computed pattern contexts.
///
/// Note: `**` is always treated as recursive doublestar here (gitignore
/// semantics). The full matcher's `ZLOB_DOUBLESTAR_RECURSIVE`-gated
/// behaviour lives in `compiled_pattern.zig`.
///
/// Supports:
/// - `*` matches any characters except `/`
/// - `?` matches exactly one character except `/`
/// - `[abc]` matches one character from the set
/// - `**` matches zero or more directories
pub fn matchGlobSimple(pattern: []const u8, path: []const u8) bool {
    if (mem.indexOf(u8, pattern, "**") == null) {
        return fnmatch_mod.fnmatch(pattern, path, .{});
    }

    var pat_segments_buf: [32][]const u8 = undefined;
    var path_segments_buf: [compiled_pattern.MAX_PATH_COMPONENTS][]const u8 = undefined;

    const pat_segments = splitPathComponentsNormalized(pattern, &pat_segments_buf) orelse return false;
    const path_segments = splitPathComponentsNormalized(path, &path_segments_buf) orelse return false;

    return matchSegmentsSimple(pat_segments, path_segments, 0, 0);
}

/// Core recursive segment matching for `**` patterns (no allocation, no PERIOD).
fn matchSegmentsSimple(
    pattern_segments: []const []const u8,
    path_segments: []const []const u8,
    initial_pat_idx: usize,
    initial_path_idx: usize,
) bool {
    var pat_idx = initial_pat_idx;
    var path_idx = initial_path_idx;

    while (true) {
        if (pat_idx >= pattern_segments.len) {
            return path_idx >= path_segments.len;
        }

        const current_pattern = pattern_segments[pat_idx];

        if (current_pattern.len == 2 and current_pattern[0] == '*' and current_pattern[1] == '*') {
            if (pat_idx + 1 >= pattern_segments.len) {
                return true;
            }

            if (matchSegmentsSimple(pattern_segments, path_segments, pat_idx + 1, path_idx)) {
                return true;
            }

            var skip: usize = 1;
            while (path_idx + skip <= path_segments.len) : (skip += 1) {
                if (matchSegmentsSimple(pattern_segments, path_segments, pat_idx + 1, path_idx + skip)) {
                    return true;
                }
            }
            return false;
        }

        if (path_idx >= path_segments.len) {
            return false;
        }

        if (!fnmatch_mod.fnmatch(current_pattern, path_segments[path_idx], .{})) {
            return false;
        }

        pat_idx += 1;
        path_idx += 1;
    }
}