/*
 * turbo_simd.c -- SIMD-accelerated parsing primitives
 *
 * Part of the TurboVM bytecode engine for high-performance log parsing.
 *
 * Copyright 2024-2026 by Advens and Jeremie Jourdin.
 * Copyright 2015-2026 by Rainer Gerhards and Adiscon GmbH.
 *
 * This file is part of liblognorm.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 *
 * A copy of the LGPL v2.1 can be found in the file "COPYING" in this distribution.
 */
#include "turbo_simd.h"
#include <string.h>

/*============================================================================
 * Backend Name
 *============================================================================*/

const char *
ln_simd_backend_name(void)
{
#if defined(LN_SIMD_SSE42)
	return "sse42";
#elif defined(LN_SIMD_NEON)
	return "neon";
#else
	return "scalar";
#endif
}

/*============================================================================
 * Character Set Helpers
 *============================================================================*/

/**
 * @brief Build a 256-byte character class lookup table.
 *
 * For fast "is char in set" checks.
 *
 * Cost: memset(256) + strlen(chars) stores = ~64 cycles on M1.
 * Called per-invocation in the scalar path. For the NEON fast path,
 * we use the nibble-parallel technique instead (32 bytes, ~10 cycles).
 */
static void __attribute__((unused))
build_char_class(const char *chars, uint8_t table[256])
{
	memset(table, 0, 256);
	while (*chars) {
		table[(uint8_t)*chars++] = 1;
	}
}

/*============================================================================
 * SSE4.2 Implementation
 *============================================================================*/

#if defined(LN_SIMD_SSE42)

/* @brief  Optimized SSE4.2 char set loader */
static inline __m128i
ln_simd_load_chars(const char *chars)
{
	uint8_t padded[16] = {0};
	if (chars) {
		strncpy((char*)padded, chars, 15);
	}
	return _mm_loadu_si128((const __m128i *)(const void *)padded);
}


/**
 * @brief Find character using SSE4.2 PCMPISTRI.
 */
size_t
ln_simd_find_char(const char *buf, size_t len, char c)
{
	__m128i needle;
	size_t i;

	if (!buf || len == 0) return 0;

	/* Create a vector of the search character */
	needle = _mm_set1_epi8(c);

	i = 0;

	/* Process 16 bytes at a time */
	while (i + 16 <= len) {
		__m128i chunk = _mm_loadu_si128((const __m128i *)(const void *)(buf + i));
		__m128i cmp = _mm_cmpeq_epi8(chunk, needle);
		int mask = _mm_movemask_epi8(cmp);

		if (mask != 0) {
			return i + __builtin_ctz(mask);
		}
		i += 16;
	}

	/* Handle remaining bytes */
	while (i < len) {
		if (buf[i] == c) return i;
		i++;
	}

	return len;
}

/**
 * @brief Find any char from set using SSE4.2.
 */
size_t
ln_simd_find_char_set(const char *buf, size_t len, const char *chars)
{
	__m128i set;
	size_t i;

	if (!buf || len == 0 || !chars || !*chars) return len;

	set = ln_simd_load_chars(chars);
	i = 0;

	while (i + 16 <= len) {
		 __m128i chunk = _mm_loadu_si128((const __m128i *)(const void *)(buf + i));
		 int index = _mm_cmpistri(set, chunk, _SIDD_UBYTE_OPS | _SIDD_CMP_EQUAL_ANY);
		 if (index < 16) return i + index;
		 i += 16;
	}
	while (i < len) {
		if (strchr(chars, buf[i])) return i;
		i++;
	}
	return len;
}

/**
 * @brief Find char NOT in set using SSE4.2.
 */
size_t
ln_simd_find_not_char_set(const char *buf, size_t len, const char *chars)
{
	uint8_t padded_set[16] = {0};
	__m128i set;
	size_t i;

	if (!buf || len == 0 || !chars) return 0;
	strncpy((char*)padded_set, chars, 15);
	set = _mm_loadu_si128((const __m128i *)(const void *)padded_set);
	i = 0;
	while (i + 16 <= len) {
		__m128i chunk = _mm_loadu_si128((const __m128i *)(const void *)(buf + i));
		/* Negative Polarity finds the first char that is NOT in the 'chars' set */
		int index = _mm_cmpistri(set, chunk,
			_SIDD_UBYTE_OPS | _SIDD_CMP_EQUAL_ANY | _SIDD_NEGATIVE_POLARITY);
		if (index < 16) return i + index;
		i += 16;
	}
	while (i < len && strchr(chars, buf[i])) i++;
	return i;
}

/**
 * @brief Skip whitespace using SSE4.2.
 */
size_t
ln_simd_skip_space(const char *buf, size_t len)
{
	__m128i ranges;
	size_t i;

	if (!buf || len == 0) return 0;

	/* Ranges: 0x09-0x0D (TAB, LF, VT, FF, CR) and 0x20 (SPACE) */
	ranges = _mm_setr_epi8(0x09, 0x0D, 0x20, 0x20, 0,0,0,0,0,0,0,0,0,0,0,0);
	i = 0;
	while (i + 16 <= len) {
		__m128i chunk = _mm_loadu_si128((const __m128i *)(const void *)(buf + i));
		/* Find first character NOT in the whitespace range */
		int index = _mm_cmpistri(ranges, chunk, _SIDD_UBYTE_OPS | _SIDD_CMP_RANGES | _SIDD_NEGATIVE_POLARITY);
		if (index < 16) return i + index;
		i += 16;
	}

	/* Handle remaining */
	while (i < len && ln_is_space(buf[i])) {
		i++;
	}

	return i;
}

#elif defined(LN_SIMD_NEON)

/*============================================================================
 * NEON Implementation (ARM64)
 *============================================================================*/

size_t
ln_simd_find_char(const char *buf, size_t len, char c)
{
	uint8x16_t needle;
	size_t i;

	/* Pre-calculate bitmask components */
	const uint8x16_t bit_weights = {1, 2, 4, 8, 16, 32, 64, 128, 1, 2, 4, 8, 16, 32, 64, 128};

	if (!buf || len == 0) return 0;
	needle = vdupq_n_u8((uint8_t)c);
	i = 0;

	while (i + 16 <= len) {
		uint8x16_t chunk = vld1q_u8((const uint8_t *)(buf + i));
		uint8x16_t cmp = vceqq_u8(chunk, needle);

		/* Convert 128-bit comparison to 16-bit mask using bit weights */
		uint8x16_t matched_bits = vandq_u8(cmp, bit_weights);
		uint8_t mask_lo = vaddv_u8(vget_low_u8(matched_bits));
		uint8_t mask_hi = vaddv_u8(vget_high_u8(matched_bits));
		uint16_t mask = (uint16_t)mask_lo | ((uint16_t)mask_hi << 8);

		if (mask != 0) {
			return i + __builtin_ctz(mask); // Instant index finding
		}
		i += 16;
	}

	while (i < len) {
		if (buf[i] == c) return i;
		i++;
	}

	return len;
}

/**
 * @brief Build NEON nibble lookup tables for character class matching.
 *
 * Technique: "Nibble-Parallel Lookup" (also used by Hyperscan, simdjson).
 *
 * For each character in the set, we encode its presence in two 16-entry
 * tables indexed by the low nibble and high nibble of the byte value.
 *
 * For a byte B, we set:
 *   lo_nibbles[B & 0x0F] |= (1 << (B >> 4))    // bit for high nibble
 *   hi_nibbles[B >> 4]   |= (1 << (B & 0x0F))   // bit for low nibble (unused)
 *
 * Actually, the simpler correct approach for ≤8 distinct high nibbles:
 *   lo_table[lo_nibble] = bitmask of which hi_nibbles have a char with this lo
 *   hi_table[hi_nibble] = bitmask of which lo_nibbles have a char with this hi
 *
 * At runtime for 16 input bytes:
 *   lo_bits = vqtbl1q_u8(lo_table, input & 0x0F)
 *   hi_bits = vqtbl1q_u8(hi_table, input >> 4)
 *   match   = lo_bits & hi_bits   // non-zero if char is in set
 *
 * This works because a byte is in the set iff both its low-nibble row
 * and high-nibble row agree on at least one bit position.
 *
 * Limitation: works perfectly when the character set spans ≤8 distinct
 * high nibbles (covers ASCII printable + control chars, which is all
 * we encounter in log parsing). For the general case we fall back to
 * the scalar table lookup.
 */
static inline int
build_neon_nibble_tables(const char *chars,
						 uint8_t lo_lut[16], uint8_t hi_lut[16])
{
	uint16_t hi_nibble_seen = 0;
	int n_hi;
	uint8_t hi_to_bit[16] = {0};
	int bit = 0;
	int h;
	const char *p;

	memset(lo_lut, 0, 16);
	memset(hi_lut, 0, 16);

	/* Count distinct high nibbles to verify the technique is applicable */
	for (p = chars; *p; p++) {
		uint8_t c = (uint8_t)*p;
		uint8_t lo = c & 0x0F;
		uint8_t hi = c >> 4;
		hi_nibble_seen |= (1u << hi);
		/*
		 * For each character c = (hi:lo):
		 *   lo_lut[lo] gets bit 'hi' set
		 *   hi_lut[hi] gets bit 'lo' set
		 *
		 * But we only have 8 bits per entry, and hi can be 0-15.
		 * Solution: use a different encoding.
		 *
		 * Actually the classic approach from Wojciech Mula:
		 *   lo_lut[lo] |= (1 << (hi & 7))   // uses 8 bits for hi groups
		 *   hi_lut[hi] |= (1 << (lo & 7))   // problem: lo can be 0-15
		 *
		 * Better: since we have uint8_t entries in the LUT, and hi can be 0-15,
		 * we need a different decomposition. Use the "compressed" approach:
		 *
		 * Assign each unique high nibble a bit position (0-7).
		 * If more than 8 unique high nibbles, fall back to scalar.
		 */
		(void)lo; (void)hi; /* used below */
	}

	/* Count distinct high nibbles */
	n_hi = __builtin_popcount(hi_nibble_seen);
	if (n_hi > 8) return -1; /* too many high nibble groups, use scalar */

	/* Build compressed hi_nibble → bit mapping */
	for (h = 0; h < 16; h++) {
		if (hi_nibble_seen & (1u << h)) {
			hi_to_bit[h] = (uint8_t)(1u << bit);
			bit++;
		}
	}

	/* Build the two LUTs */
	for (p = chars; *p; p++) {
		uint8_t c = (uint8_t)*p;
		uint8_t lo = c & 0x0F;
		uint8_t hi = c >> 4;
		lo_lut[lo] |= hi_to_bit[hi];  /* "which hi-groups have this lo nibble" */
		hi_lut[hi] |= hi_to_bit[hi];  /* "which hi-group is this" */
	}

	/*
	 * Fix: hi_lut should answer "what bit mask does this hi-nibble produce?"
	 * so that AND with lo_lut gives non-zero iff the (hi,lo) combo is in set.
	 *
	 * Actually, rethink: for input byte B = (hi_B:lo_B):
	 *   lo_bits = lo_lut[lo_B]  → bitmask of hi-groups that have lo_B
	 *   hi_bits = hi_lut[hi_B]  → bitmask of hi-group that hi_B belongs to
	 *   match = lo_bits & hi_bits → non-zero iff (hi_B, lo_B) is in the set
	 *
	 * This is correct! hi_lut[hi_B] = hi_to_bit[hi_B] which is the single
	 * bit for hi_B's group. lo_lut[lo_B] has that bit set iff some char
	 * with lo nibble = lo_B exists in hi_B's group.
	 */

	return 0;
}

/**
 * @brief Find any char from set using NEON nibble-parallel lookup.
 *
 * Processes 16 bytes per iteration using vqtbl1q_u8 for parallel
 * character class testing. Each iteration:
 *   1. Load 16 input bytes
 *   2. Split each byte into (hi_nibble, lo_nibble) via vshrq/vandq
 *   3. Lookup both nibbles through 16-byte LUTs via vqtbl1q_u8
 *   4. AND results → non-zero lanes are matches
 *   5. Reduce to bitmask and find first set bit
 *
 * Throughput: ~16 bytes per ~6 NEON instructions ≈ 2.5 bytes/cycle on M1.
 * Previous scalar: ~1 byte per ~3 instructions ≈ 0.3 bytes/cycle.
 * Speedup: ~8x for the inner loop.
 */
size_t
ln_simd_find_char_set(const char *buf, size_t len, const char *chars)
{
	uint8_t lo_lut_data[16], hi_lut_data[16];
	int neon_ok;
	uint8_t table[256];
	size_t i;

	if (!buf || len == 0 || !chars || !*chars) return len;

	/* Build NEON nibble lookup tables */
	neon_ok = build_neon_nibble_tables(chars, lo_lut_data, hi_lut_data);

	if (neon_ok == 0) {
		/* NEON fast path: nibble-parallel lookup */
		uint8x16_t lo_lut = vld1q_u8(lo_lut_data);
		uint8x16_t hi_lut = vld1q_u8(hi_lut_data);
		uint8x16_t lo_mask = vdupq_n_u8(0x0F);

		/* Bit-weight for bitmask extraction */
		const uint8x16_t bit_weights = {
			1, 2, 4, 8, 16, 32, 64, 128,
			1, 2, 4, 8, 16, 32, 64, 128
		};

		i = 0;
		while (i + 16 <= len) {
			uint8x16_t chunk = vld1q_u8((const uint8_t *)(buf + i));

			/* Split into nibbles */
			uint8x16_t lo_nibbles = vandq_u8(chunk, lo_mask);
			uint8x16_t hi_nibbles = vshrq_n_u8(chunk, 4);

			/* Parallel table lookup */
			uint8x16_t lo_bits = vqtbl1q_u8(lo_lut, lo_nibbles);
			uint8x16_t hi_bits = vqtbl1q_u8(hi_lut, hi_nibbles);

			/* AND → non-zero if char is in set */
			uint8x16_t matches = vandq_u8(lo_bits, hi_bits);

			/* Convert to bitmask */
			uint8x16_t matched_bits = vandq_u8(
				vcgtq_u8(matches, vdupq_n_u8(0)),  /* non-zero → 0xFF */
				bit_weights
			);
			uint8_t mask_lo = vaddv_u8(vget_low_u8(matched_bits));
			uint8_t mask_hi = vaddv_u8(vget_high_u8(matched_bits));
			uint16_t mask = (uint16_t)mask_lo | ((uint16_t)mask_hi << 8);

			if (mask != 0) {
				return i + __builtin_ctz(mask);
			}
			i += 16;
		}

		/* Scalar tail */
		build_char_class(chars, table);
		while (i < len) {
			if (table[(uint8_t)buf[i]]) return i;
			i++;
		}
		return len;
	}

	/* Fallback: scalar with lookup table (> 8 high nibble groups) */
	build_char_class(chars, table);
	for (i = 0; i < len; i++) {
		if (table[(uint8_t)buf[i]]) return i;
	}
	return len;
}

/**
 * @brief Find first char NOT in set using NEON nibble-parallel lookup.
 *
 * Same nibble-parallel technique as find_char_set, but inverted:
 * we look for the first byte where the AND of lo_bits and hi_bits is ZERO.
 */
size_t
ln_simd_find_not_char_set(const char *buf, size_t len, const char *chars)
{
	uint8_t lo_lut_data[16], hi_lut_data[16];
	int neon_ok;
	uint8_t table[256];
	size_t i;

	if (!buf || len == 0) return 0;
	if (!chars || !*chars) return 0;

	/* Build NEON nibble lookup tables */
	neon_ok = build_neon_nibble_tables(chars, lo_lut_data, hi_lut_data);

	if (neon_ok == 0) {
		/* NEON fast path */
		uint8x16_t lo_lut = vld1q_u8(lo_lut_data);
		uint8x16_t hi_lut = vld1q_u8(hi_lut_data);
		uint8x16_t lo_mask = vdupq_n_u8(0x0F);

		const uint8x16_t bit_weights = {
			1, 2, 4, 8, 16, 32, 64, 128,
			1, 2, 4, 8, 16, 32, 64, 128
		};

		i = 0;
		while (i + 16 <= len) {
			uint8x16_t chunk = vld1q_u8((const uint8_t *)(buf + i));

			uint8x16_t lo_nibbles = vandq_u8(chunk, lo_mask);
			uint8x16_t hi_nibbles = vshrq_n_u8(chunk, 4);

			uint8x16_t lo_bits = vqtbl1q_u8(lo_lut, lo_nibbles);
			uint8x16_t hi_bits = vqtbl1q_u8(hi_lut, hi_nibbles);

			/* AND → non-zero if char IS in set */
			uint8x16_t in_set = vandq_u8(lo_bits, hi_bits);

			/* We want NOT in set → lanes where in_set == 0 */
			uint8x16_t not_in_set = vceqq_u8(in_set, vdupq_n_u8(0));

			uint8x16_t matched_bits = vandq_u8(not_in_set, bit_weights);
			uint8_t mask_lo = vaddv_u8(vget_low_u8(matched_bits));
			uint8_t mask_hi = vaddv_u8(vget_high_u8(matched_bits));
			uint16_t mask = (uint16_t)mask_lo | ((uint16_t)mask_hi << 8);

			if (mask != 0) {
				return i + __builtin_ctz(mask);
			}
			i += 16;
		}

		/* Scalar tail */
		build_char_class(chars, table);
		while (i < len) {
			if (!table[(uint8_t)buf[i]]) return i;
			i++;
		}
		return len;
	}

	/* Fallback: scalar */
	build_char_class(chars, table);
	for (i = 0; i < len; i++) {
		if (!table[(uint8_t)buf[i]]) return i;
	}
	return len;
}

size_t
ln_simd_skip_space(const char *buf, size_t len)
{
	uint8x16_t space;
	uint8x16_t tab_range_lo;
	uint8x16_t tab_range_hi;

	/* Pre-calculated weights to create a bitmask from vector */
	const uint8x16_t bit_weights = {1, 2, 4, 8, 16, 32, 64, 128, 1, 2, 4, 8, 16, 32, 64, 128};
	size_t i = 0;

	if (!buf || len == 0) return 0;
	space = vdupq_n_u8(' ');
	tab_range_lo = vdupq_n_u8(0x09);
	tab_range_hi = vdupq_n_u8(0x0D);

	while (i + 16 <= len) {
		uint8x16_t chunk = vld1q_u8((const uint8_t *)(buf + i));

		/* Check for ' ' OR (>= 0x09 AND <= 0x0D) */
		uint8x16_t is_space = vceqq_u8(chunk, space);
		uint8x16_t in_tab_range = vandq_u8(vcgeq_u8(chunk, tab_range_lo), vcleq_u8(chunk, tab_range_hi));
		uint8x16_t is_ws = vorrq_u8(is_space, in_tab_range);

		/* Create a 16-bit mask from the 128-bit vector */
		uint8x16_t matched_bits = vandq_u8(is_ws, bit_weights);
		uint16_t mask = (uint16_t)vaddv_u8(vget_low_u8(matched_bits)) |
						((uint16_t)vaddv_u8(vget_high_u8(matched_bits)) << 8);

		if (mask != 0xFFFF) {
			/* Find first zero bit (first non-whitespace) */
			return i + __builtin_ctz(~mask);
		}
		i += 16;
	}
	while (i < len && ln_is_space(buf[i])) i++;
	return i;
}

#else

/*============================================================================
 * Scalar Fallback Implementation
 *============================================================================*/

/*----------------------------------------------------------------------------
 * Precomputed Character Class Cache (Thread-Local)
 *
 * The VM repeatedly calls find_char_set / find_not_char_set with the
 * same character set strings (e.g., " \t\n\r" for word boundaries).
 * Instead of rebuilding the 256-byte table on every call, we cache
 * the last-used table per thread.
 *
 * This is a simple 1-entry cache: if the pointer matches, reuse.
 * Since instruction data pointers are stable for the program lifetime,
 * pointer comparison is sufficient (no strcmp needed).
 *
 * Cache hit rate in practice: >95% (word extraction dominates).
 *----------------------------------------------------------------------------*/

#if defined(__GNUC__) || defined(__clang__)
#define THREAD_LOCAL __thread
#elif defined(_MSC_VER)
#define THREAD_LOCAL __declspec(thread)
#else
#define THREAD_LOCAL /* nothing — falls back to rebuild every time */
#endif

static THREAD_LOCAL const char *s_cached_chars = NULL;
static THREAD_LOCAL uint8_t     s_cached_table[256];

/**
 * @brief Get or build a character class table, with 1-entry TLS cache.
 *
 * @param[in]  chars  Null-terminated character set string (pointer-stable)
 * @return Pointer to the 256-byte TLS-cached table
 */
static inline const uint8_t *
get_char_class(const char *chars)
{
	if (chars == s_cached_chars && s_cached_chars != NULL) {
		return s_cached_table;  /* Cache hit — same pointer, same data */
	}
	/* Cache miss: build and cache */
	build_char_class(chars, s_cached_table);
	s_cached_chars = chars;
	return s_cached_table;
}

size_t
ln_simd_find_char(const char *buf, size_t len, char c)
{
	if (!buf) return 0;

	for (size_t i = 0; i < len; i++) {
		if (buf[i] == c) return i;
	}
	return len;
}

size_t
ln_simd_find_char_set(const char *buf, size_t len, const char *chars)
{
	const uint8_t *table;
	size_t i;

	if (!buf || !chars || !*chars) return len;

	table = get_char_class(chars);

	for (i = 0; i < len; i++) {
		if (table[(uint8_t)buf[i]]) return i;
	}
	return len;
}

size_t
ln_simd_find_not_char_set(const char *buf, size_t len, const char *chars)
{
	const uint8_t *table;
	size_t i;

	if (!buf || len == 0) return 0;
	if (!chars || !*chars) return 0;

	table = get_char_class(chars);

	for (i = 0; i < len; i++) {
		if (!table[(uint8_t)buf[i]]) return i;
	}
	return len;
}

size_t
ln_simd_skip_space(const char *buf, size_t len)
{
	size_t i;

	if (!buf) return 0;

	i = 0;
	while (i < len && ln_is_space(buf[i])) {
		i++;
	}
	return i;
}

#endif /* Architecture selection */

/*============================================================================
 * Common Implementation (uses architecture-specific primitives)
 *============================================================================*/

size_t
ln_simd_skip_chars(const char *buf, size_t len, const char *chars)
{
	return ln_simd_find_not_char_set(buf, len, chars);
}

/*============================================================================
 * Field Extraction
 *============================================================================*/

int
ln_simd_word(const char *buf, size_t len, ln_span_t *span)
{
	 size_t word_len;

	 if (!buf || !span) return LN_SIMD_EINVAL;

	 /* STRICT COMPLIANCE: Do NOT skip leading whitespace.
	  * The VM handles skipping via OP_SKIP_SPACE.
	  * If we start on a space, it's an empty word (or error).
	  */
	 if (len > 0 && ln_is_space(buf[0])) {
		  /* Legacy behavior: if starting on space, return error or empty.
		   * parser.c ln_v2_parseWord returns -1 if i==0.
		   */
		  return LN_SIMD_ENOTFOUND;
	 }

	 /* Find end of word (next whitespace) */
	 word_len = ln_simd_find_char_set(buf, len, " \t\n\r");

	 if (word_len == 0 && len > 0 && !ln_is_space(buf[0])) {
		  /* No separators found, word is entire buffer */
		  word_len = len;
	 }

	 span->start = buf;
	 span->len = word_len;
	 span->consumed = word_len; /* Stop AT the space */

	 return LN_SIMD_OK;
}

int
ln_simd_char_to(const char *buf, size_t len, char delim, ln_span_t *span)
{
	size_t pos;

	if (!buf || !span) return LN_SIMD_EINVAL;

	pos = ln_simd_find_char(buf, len, delim);

	span->start = buf;
	span->len = pos;

	if (pos < len) {
		/*
		 * We must stop AT the delimiter so the VM's next instruction (OP_LITERAL)
		 * can match and consume it. This aligns with parser.c behavior.
		 */
		span->consumed = pos;
		return LN_SIMD_OK;
	} else {
		span->consumed = len;
		return LN_SIMD_ENOTFOUND;
	}
}

int
ln_simd_string_to(const char *buf, size_t len,
				  const char *delim, size_t delim_len,
				  ln_span_t *span)
{
	char first;
	size_t i;

	if (!buf || !span || !delim) return LN_SIMD_EINVAL;
	if (delim_len == 0) return LN_SIMD_EINVAL;

	if (delim_len == 1) {
		return ln_simd_char_to(buf, len, delim[0], span);
	}

	first = delim[0];
	i = 0;

	while (i + delim_len <= len) {
		size_t pos = ln_simd_find_char(buf + i, len - i, first);
		if (pos >= len - i) break;

		i += pos;

		if (i + delim_len <= len && memcmp(buf + i, delim, delim_len) == 0) {
			span->start = buf;
			span->len = i;
			span->consumed = i; /* Stop AT the delimiter */
			return LN_SIMD_OK;
		}
		i++;
	}

	span->start = buf;
	span->len = len;
	span->consumed = len;
	return LN_SIMD_ENOTFOUND;
}

int
ln_simd_quoted(const char *buf, size_t len, ln_span_t *span)
{
	char quote;
	size_t i;
	size_t content_start;
	bool escaped;

	if (!buf || !span || len < 2) return LN_SIMD_EINVAL;

	quote = buf[0];
	if (quote != '"' && quote != '\'') {
		return LN_SIMD_EFORMAT;
	}

	i = 1;
	content_start = 1;
	escaped = false;

	while (i < len) {
		char c = buf[i];

		if (escaped) {
			escaped = false;
			i++;
			continue;
		}

		if (c == '\\') {
			escaped = true;
			i++;
			continue;
		}

		if (c == quote) {
			/* Found closing quote */
			span->start = buf + content_start;
			span->len = i - content_start;
			span->consumed = i + 1;
			return LN_SIMD_OK;
		}

		i++;
	}

	/* No closing quote found */
	return LN_SIMD_EFORMAT;
}

int
ln_simd_bracketed(const char *buf, size_t len,
				  char open, char close, ln_span_t *span)
{
	int depth;
	size_t i;

	if (!buf || !span || len < 2) return LN_SIMD_EINVAL;

	if (buf[0] != open) {
		return LN_SIMD_EFORMAT;
	}

	depth = 1;
	i = 1;

	while (i < len && depth > 0) {
		char c = buf[i];

		if (c == open) {
			depth++;
		} else if (c == close) {
			depth--;
		}

		if (depth > 0) {
			i++;
		}
	}

	if (depth != 0) {
		return LN_SIMD_EFORMAT;
	}

	span->start = buf + 1;
	span->len = i - 1;
	span->consumed = i + 1;
	return LN_SIMD_OK;
}

/*============================================================================
 * Numeric Parsing
 *============================================================================*/

int
ln_simd_number(const char *buf, size_t len, ln_number_t *result)
{
	size_t i;
	uint64_t val;
	const uint64_t overflow_threshold = UINT64_MAX / 10;

	if (!buf || !result) return LN_SIMD_EINVAL;

	result->value = 0;
	result->consumed = 0;
	result->negative = false;
	result->overflow = false;

	if (len == 0) return LN_SIMD_EFORMAT;

	i = 0;

	/* Handle sign */
	if (buf[i] == '-') {
		result->negative = true;
		i++;
	} else if (buf[i] == '+') {
		i++;
	}

	if (i >= len || !ln_is_digit(buf[i])) {
		return LN_SIMD_EFORMAT;
	}

	/* Parse digits */
	val = 0;

	while (i < len && ln_is_digit(buf[i])) {
		uint8_t digit = buf[i] - '0';

		/* Check for overflow */
		if (val > overflow_threshold ||
			(val == overflow_threshold && digit > UINT64_MAX % 10)) {
			result->overflow = true;
			/* Continue parsing to consume all digits */
		}

		val = val * 10 + digit;
		i++;
	}

	result->consumed = i;

	if (result->negative) {
		if (val > (uint64_t)INT64_MAX + 1) {
			result->overflow = true;
		}
		result->value = -(int64_t)val;
	} else {
		if (val > (uint64_t)INT64_MAX) {
			result->overflow = true;
		}
		result->value = (int64_t)val;
	}

	return LN_SIMD_OK;
}

int
ln_simd_unsigned(const char *buf, size_t len, ln_number_t *result)
{
	size_t i;
	uint64_t val;
	const uint64_t overflow_threshold = UINT64_MAX / 10;

	if (!buf || !result) return LN_SIMD_EINVAL;

	result->value = 0;
	result->consumed = 0;
	result->negative = false;
	result->overflow = false;

	if (len == 0) return LN_SIMD_EFORMAT;

	i = 0;

	/* Optional plus sign */
	if (buf[i] == '+') {
		i++;
	} else if (buf[i] == '-') {
		return LN_SIMD_EFORMAT;  /* Negative not allowed */
	}

	if (i >= len || !ln_is_digit(buf[i])) {
		return LN_SIMD_EFORMAT;
	}

	val = 0;

	while (i < len && ln_is_digit(buf[i])) {
		uint8_t digit = buf[i] - '0';

		if (val > overflow_threshold ||
			(val == overflow_threshold && digit > UINT64_MAX % 10)) {
			result->overflow = true;
		}

		val = val * 10 + digit;
		i++;
	}

	result->consumed = i;
	result->value = (int64_t)val;

	return LN_SIMD_OK;
}

int
ln_simd_hex(const char *buf, size_t len, ln_number_t *result)
{
	size_t i;
	uint64_t val;
	bool has_digit;

	if (!buf || !result) return LN_SIMD_EINVAL;

	result->value = 0;
	result->consumed = 0;
	result->negative = false;
	result->overflow = false;

	if (len == 0) return LN_SIMD_EFORMAT;

	i = 0;

	/* Skip optional 0x/0X prefix */
	if (len >= 2 && buf[0] == '0' && (buf[1] == 'x' || buf[1] == 'X')) {
		i = 2;
	}

	if (i >= len) return LN_SIMD_EFORMAT;

	val = 0;
	has_digit = false;

	while (i < len) {
		char c = buf[i];
		uint8_t digit;

		if (c >= '0' && c <= '9') {
			digit = c - '0';
		} else if (c >= 'A' && c <= 'F') {
			digit = c - 'A' + 10;
		} else if (c >= 'a' && c <= 'f') {
			digit = c - 'a' + 10;
		} else {
			break;
		}

		/* Check for overflow (shift by 4 bits) */
		if (val >> 60) {
			result->overflow = true;
		}

		val = (val << 4) | digit;
		has_digit = true;
		i++;
	}

	if (!has_digit) return LN_SIMD_EFORMAT;

	result->consumed = i;
	result->value = (int64_t)val;

	return LN_SIMD_OK;
}

/*============================================================================
 * IPv4 Parsing
 *============================================================================*/

int
ln_simd_ipv4(const char *buf, size_t len, ln_ipv4_t *result)
{
	size_t i;
	int octet;

	if (!buf || !result) return LN_SIMD_EINVAL;

	result->addr = 0;
	result->consumed = 0;
	result->valid = false;
	memset(result->octets, 0, 4);

	if (len < 7) return LN_SIMD_EFORMAT;  /* Minimum: "0.0.0.0" */

	i = 0;

	for (octet = 0; octet < 4; octet++) {
		unsigned int val = 0;
		int digits = 0;

		/* Parse octet value */
		if (i >= len || !ln_is_digit(buf[i])) {
			return LN_SIMD_EFORMAT;
		}

		while (i < len && ln_is_digit(buf[i])) {
			val = val * 10 + (buf[i] - '0');
			digits++;
			i++;

			if (digits > 3 || val > 255) {
				return LN_SIMD_EFORMAT;
			}
		}

		result->octets[octet] = (uint8_t)val;

		/* Expect dot between octets (except after last) */
		if (octet < 3) {
			if (i >= len || buf[i] != '.') {
				return LN_SIMD_EFORMAT;
			}
			i++;  /* Skip dot */
		}
	}

	/* Build network-order address */
	result->addr = ((uint32_t)result->octets[0] << 24) |
				   ((uint32_t)result->octets[1] << 16) |
				   ((uint32_t)result->octets[2] << 8) |
				   ((uint32_t)result->octets[3]);

	result->consumed = i;
	result->valid = true;

	return LN_SIMD_OK;
}

int
ln_simd_ipv4_port(const char *buf, size_t len,
				  ln_ipv4_t *ip, uint16_t *port)
{
	int r;
	size_t i;

	if (!buf || !ip || !port) return LN_SIMD_EINVAL;

	*port = 0;

	/* Parse IP first */
	r = ln_simd_ipv4(buf, len, ip);
	if (r != LN_SIMD_OK) return r;

	/* Check for optional port */
	i = ip->consumed;

	if (i < len && buf[i] == ':') {
		unsigned int port_val = 0;

		i++;  /* Skip colon */

		if (i >= len || !ln_is_digit(buf[i])) {
			return LN_SIMD_OK;  /* No port after colon - still valid IP */
		}

		while (i < len && ln_is_digit(buf[i])) {
			port_val = port_val * 10 + (buf[i] - '0');
			if (port_val > 65535) {
				return LN_SIMD_EFORMAT;
			}
			i++;
		}

		*port = (uint16_t)port_val;
		ip->consumed = i;
	}

	return LN_SIMD_OK;
}

/*============================================================================
 * Timestamp Parsing
 *============================================================================*/

/* Month name lookup */
static const char *months[] = {
	"Jan", "Feb", "Mar", "Apr", "May", "Jun",
	"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
};

/**
 * @brief Parse RFC 3164 timestamp: "Jan 15 10:30:45"
 */
static int
parse_rfc3164(const char *buf, size_t len, ln_span_t *span)
{
	int month = -1;
	int m;
	size_t i;

	/* Minimum length: "Jan  1 0:0:0" = 12 */
	if (len < 12) return LN_SIMD_EFORMAT;

	/* Check month */
	for (m = 0; m < 12; m++) {
		if (memcmp(buf, months[m], 3) == 0) {
			month = m;
			break;
		}
	}

	if (month < 0) return LN_SIMD_EFORMAT;

	/* Skip month and space(s) */
	i = 3;
	while (i < len && buf[i] == ' ') i++;

	/* Parse day (1 or 2 digits) */
	if (!ln_is_digit(buf[i])) return LN_SIMD_EFORMAT;
	while (i < len && ln_is_digit(buf[i])) i++;

	/* Space before time */
	if (i >= len || buf[i] != ' ') return LN_SIMD_EFORMAT;
	i++;

	/* Parse time HH:MM:SS */

	/* Hours */
	if (i + 2 > len || !ln_is_digit(buf[i])) return LN_SIMD_EFORMAT;
	i++;
	if (ln_is_digit(buf[i])) i++;

	if (i >= len || buf[i] != ':') return LN_SIMD_EFORMAT;
	i++;

	/* Minutes */
	if (i + 2 > len || !ln_is_digit(buf[i]) || !ln_is_digit(buf[i+1]))
		return LN_SIMD_EFORMAT;
	i += 2;

	if (i >= len || buf[i] != ':') return LN_SIMD_EFORMAT;
	i++;

	/* Seconds */
	if (i + 2 > len || !ln_is_digit(buf[i]) || !ln_is_digit(buf[i+1]))
		return LN_SIMD_EFORMAT;
	i += 2;

	span->start = buf;
	span->len = i;
	span->consumed = i;

	return LN_SIMD_OK;
}

/**
 * @brief Parse RFC 5424 / ISO 8601 timestamp: "2024-01-15T10:30:45.123Z"
 */
static int
parse_rfc5424(const char *buf, size_t len, ln_span_t *span)
{
	int j;
	size_t i;

	/* Minimum: "2024-01-15T10:30:45" = 19 */
	if (len < 19) return LN_SIMD_EFORMAT;

	/* Quick validation of format */
	if (buf[4] != '-' || buf[7] != '-' ||
		(buf[10] != 'T' && buf[10] != ' ') ||
		buf[13] != ':' || buf[16] != ':') {
		return LN_SIMD_EFORMAT;
	}

	/* Validate digits */
	for (j = 0; j < 19; j++) {
		if (j == 4 || j == 7 || j == 10 || j == 13 || j == 16) continue;
		if (!ln_is_digit(buf[j])) return LN_SIMD_EFORMAT;
	}

	i = 19;

	/* Optional fractional seconds */
	if (i < len && buf[i] == '.') {
		i++;
		while (i < len && ln_is_digit(buf[i])) i++;
	}

	/* Optional timezone */
	if (i < len) {
		if (buf[i] == 'Z') {
			i++;
		} else if (buf[i] == '+' || buf[i] == '-') {
			i++;
			/* HH:MM */
			if (i + 5 <= len && ln_is_digit(buf[i]) && ln_is_digit(buf[i+1]) &&
				buf[i+2] == ':' && ln_is_digit(buf[i+3]) && ln_is_digit(buf[i+4])) {
				i += 5;
			}
		}
	}

	span->start = buf;
	span->len = i;
	span->consumed = i;

	return LN_SIMD_OK;
}

int
ln_simd_timestamp(const char *buf, size_t len,
				  ln_span_t *span, int64_t *epoch_ms)
{
	if (!buf || !span) return LN_SIMD_EINVAL;

	if (epoch_ms) *epoch_ms = 0;  /* Not implemented for now */

	/* Try RFC 5424 first (starts with digit) */
	if (len >= 4 && ln_is_digit(buf[0])) {
		if (parse_rfc5424(buf, len, span) == LN_SIMD_OK) {
			return LN_SIMD_OK;
		}
	}

	/* Try RFC 3164 (starts with month name) */
	if (len >= 3 && buf[0] >= 'A' && buf[0] <= 'S') {
		if (parse_rfc3164(buf, len, span) == LN_SIMD_OK) {
			return LN_SIMD_OK;
		}
	}

	return LN_SIMD_EFORMAT;
}

/*============================================================================
 * Utility Functions
 *============================================================================*/

size_t
ln_simd_unescape(char *buf, size_t len)
{
	size_t read;
	size_t write;

	if (!buf || len == 0) return 0;

	read = 0;
	write = 0;

	while (read < len) {
		if (buf[read] == '\\' && read + 1 < len) {
			char next = buf[read + 1];
			char replacement;

			switch (next) {
			case '\\': replacement = '\\'; break;
			case '"':  replacement = '"';  break;
			case '\'': replacement = '\''; break;
			case 'n':  replacement = '\n'; break;
			case 'r':  replacement = '\r'; break;
			case 't':  replacement = '\t'; break;
			case '0':  replacement = '\0'; break;
			case 'x':
				/* Hex escape \xHH */
				if (read + 3 < len) {
					char h1 = buf[read + 2];
					char h2 = buf[read + 3];
					int v1 = -1, v2 = -1;

					if (h1 >= '0' && h1 <= '9') v1 = h1 - '0';
					else if (h1 >= 'A' && h1 <= 'F') v1 = h1 - 'A' + 10;
					else if (h1 >= 'a' && h1 <= 'f') v1 = h1 - 'a' + 10;

					if (h2 >= '0' && h2 <= '9') v2 = h2 - '0';
					else if (h2 >= 'A' && h2 <= 'F') v2 = h2 - 'A' + 10;
					else if (h2 >= 'a' && h2 <= 'f') v2 = h2 - 'a' + 10;

					if (v1 >= 0 && v2 >= 0) {
						buf[write++] = (char)((v1 << 4) | v2);
						read += 4;
						continue;
					}
				}
				/* Invalid hex - keep as-is */
				buf[write++] = buf[read++];
				continue;
			default:
				/* Unknown escape - keep backslash and char */
				buf[write++] = buf[read++];
				continue;
			}

			buf[write++] = replacement;
			read += 2;
		} else {
			buf[write++] = buf[read++];
		}
	}

	return write;
}
