#if SIMDUTF_FEATURE_ASCII
simdutf_warn_unused bool
implementation::validate_ascii(const char *src, size_t len) const noexcept {
  size_t vlmax = __riscv_vsetvlmax_e8m8();
  vint8m8_t mask = __riscv_vmv_v_x_i8m8(0, vlmax);
  for (size_t vl; len > 0; len -= vl, src += vl) {
    vl = __riscv_vsetvl_e8m8(len);
    vint8m8_t v = __riscv_vle8_v_i8m8((int8_t *)src, vl);
    mask = __riscv_vor_vv_i8m8_tu(mask, mask, v, vl);
  }
  return __riscv_vfirst_m_b1(__riscv_vmslt_vx_i8m8_b1(mask, 0, vlmax), vlmax) <
         0;
}

simdutf_warn_unused result implementation::validate_ascii_with_errors(
    const char *src, size_t len) const noexcept {
  const char *beg = src;
  for (size_t vl; len > 0; len -= vl, src += vl) {
    vl = __riscv_vsetvl_e8m8(len);
    vint8m8_t v = __riscv_vle8_v_i8m8((int8_t *)src, vl);
    long idx = __riscv_vfirst_m_b1(__riscv_vmslt_vx_i8m8_b1(v, 0, vl), vl);
    if (idx >= 0)
      return result(error_code::TOO_LARGE, src - beg + idx);
  }
  return result(error_code::SUCCESS, src - beg);
}
#endif // SIMDUTF_FEATURE_ASCII
#if SIMDUTF_FEATURE_UTF16 && SIMDUTF_FEATURE_ASCII
template <simdutf_ByteFlip bflip>
simdutf_really_inline bool rvv_validate_utf16_as_ascii(const char16_t *buf,
                                                       size_t len) noexcept {
  const char16_t *src = buf;
  for (size_t vl; len > 0; len -= vl, src += vl) {
    vl = __riscv_vsetvl_e16m8(len);
    vuint16m8_t v = __riscv_vle16_v_u16m8((uint16_t *)src, vl);
    v = simdutf_byteflip<bflip>(v, vl);
    long idx = __riscv_vfirst_m_b2(__riscv_vmsgtu_vx_u16m8_b2(v, 0x7f, vl), vl);
    if (idx >= 0)
      return false;
  }
  return true;
}
simdutf_warn_unused bool
implementation::validate_utf16le_as_ascii(const char16_t *buf,
                                          size_t len) const noexcept {
  return rvv_validate_utf16_as_ascii<simdutf_ByteFlip::NONE>(buf, len);
}

simdutf_warn_unused bool
implementation::validate_utf16be_as_ascii(const char16_t *buf,
                                          size_t len) const noexcept {
  if (supports_zvbb())
    return rvv_validate_utf16_as_ascii<simdutf_ByteFlip::ZVBB>(buf, len);
  else
    return rvv_validate_utf16_as_ascii<simdutf_ByteFlip::V>(buf, len);
}
#endif // SIMDUTF_FEATURE_UTF16 && SIMDUTF_FEATURE_ASCII
#if SIMDUTF_FEATURE_UTF8 || SIMDUTF_FEATURE_DETECT_ENCODING
/* Returns a close estimation of the number of valid UTF-8 bytes up to the
 * first invalid one, but never overestimating. */
simdutf_really_inline static size_t rvv_count_valid_utf8(const char *src,
                                                         size_t len) {
  const char *beg = src;
  if (len < 32)
    return 0;

  /* validate first three bytes */
  {
    size_t idx = 3;
    while (idx < len && (uint8_t(src[idx]) >> 6) == 0b10)
      ++idx;
    if (idx > 3 + 3 || !scalar::utf8::validate(src, idx))
      return 0;
  }

  static const uint64_t err1m[] = {0x0202020202020202, 0x4915012180808080};
  static const uint64_t err2m[] = {0xCBCBCB8B8383A3E7, 0xCBCBDBCBCBCBCBCB};
  static const uint64_t err3m[] = {0x0101010101010101, 0X01010101BABAAEE6};

  const vuint8m1_t err1tbl =
      __riscv_vreinterpret_v_u64m1_u8m1(__riscv_vle64_v_u64m1(err1m, 2));
  const vuint8m1_t err2tbl =
      __riscv_vreinterpret_v_u64m1_u8m1(__riscv_vle64_v_u64m1(err2m, 2));
  const vuint8m1_t err3tbl =
      __riscv_vreinterpret_v_u64m1_u8m1(__riscv_vle64_v_u64m1(err3m, 2));

  size_t tail = 3;
  size_t n = len - tail;

  for (size_t vl; n > 0; n -= vl, src += vl) {
    vl = __riscv_vsetvl_e8m4(n);
    vuint8m4_t v0 = __riscv_vle8_v_u8m4((uint8_t const *)src, vl);

    uint8_t next0 = src[vl + 0];
    uint8_t next1 = src[vl + 1];
    uint8_t next2 = src[vl + 2];

    /* fast path: ASCII */
    if (__riscv_vfirst_m_b2(__riscv_vmsgtu_vx_u8m4_b2(v0, 0b01111111, vl), vl) <
            0 &&
        (next0 | next1 | next2) < 0b10000000)
      continue;

    /* see "Validating UTF-8 In Less Than One Instruction Per Byte"
     * https://arxiv.org/abs/2010.03090 */
    vuint8m4_t v1 = __riscv_vslide1down_vx_u8m4(v0, next0, vl);
    vuint8m4_t v2 = __riscv_vslide1down_vx_u8m4(v1, next1, vl);

    vuint8m4_t v2_hi_nibble = __riscv_vsrl_vx_u8m4(v2, 4, vl);
    vuint8m4_t v3_hi_nibble =
        __riscv_vslide1down_vx_u8m4(v2_hi_nibble, next2 >> 4, vl);

    vuint8m4_t idx2 = __riscv_vand_vx_u8m4(v2, 0xF, vl);
    vuint8m4_t idx1 = v2_hi_nibble;
    vuint8m4_t idx3 = v3_hi_nibble;

    vuint8m4_t err1 = simdutf_vrgather_u8m1x4(err1tbl, idx1);
    vuint8m4_t err2 = simdutf_vrgather_u8m1x4(err2tbl, idx2);
    vuint8m4_t err3 = simdutf_vrgather_u8m1x4(err3tbl, idx3);
    vint8m4_t errs = __riscv_vreinterpret_v_u8m4_i8m4(
        __riscv_vand_vv_u8m4(__riscv_vand_vv_u8m4(err1, err2, vl), err3, vl));

    vbool2_t is_3 = __riscv_vmsgtu_vx_u8m4_b2(v1, 0b11100000 - 1, vl);
    vbool2_t is_4 = __riscv_vmsgtu_vx_u8m4_b2(v0, 0b11110000 - 1, vl);
    vbool2_t is_34 = __riscv_vmor_mm_b2(is_3, is_4, vl);
    vbool2_t err34 =
        __riscv_vmxor_mm_b2(is_34, __riscv_vmslt_vx_i8m4_b2(errs, 0, vl), vl);
    vbool2_t errm =
        __riscv_vmor_mm_b2(__riscv_vmsgt_vx_i8m4_b2(errs, 0, vl), err34, vl);
    if (__riscv_vfirst_m_b2(errm, vl) >= 0)
      break;
  }

  /* we need to validate the last character */
  while (tail < len && (uint8_t(src[0]) >> 6) == 0b10)
    --src, ++tail;
  return src - beg;
}

simdutf_warn_unused bool
implementation::validate_utf8(const char *src, size_t len) const noexcept {
  size_t count = rvv_count_valid_utf8(src, len);
  return scalar::utf8::validate(src + count, len - count);
}
#endif // SIMDUTF_FEATURE_UTF8 || SIMDUTF_FEATURE_DETECT_ENCODING

#if SIMDUTF_FEATURE_UTF8
simdutf_warn_unused result implementation::validate_utf8_with_errors(
    const char *src, size_t len) const noexcept {
  size_t count = rvv_count_valid_utf8(src, len);
  result res = scalar::utf8::validate_with_errors(src + count, len - count);
  return result(res.error, count + res.count);
}
#endif // SIMDUTF_FEATURE_UTF8

#if SIMDUTF_FEATURE_UTF16 || SIMDUTF_FEATURE_DETECT_ENCODING
template <simdutf_ByteFlip bflip>
simdutf_really_inline static result
rvv_validate_utf16_with_errors(const char16_t *src, size_t len) {
  const char16_t *beg = src;

  const uint16_t mask = simdutf_byteflip<bflip>(0xfc00);
  const uint16_t hi_surrogate = simdutf_byteflip<bflip>(0xd800);
  const uint16_t lo_surrogate = simdutf_byteflip<bflip>(0xdc00);

  uint16_t last = 0;
  for (size_t vl; len > 0; len -= vl, src += vl, last = src[-1]) {
    vl = __riscv_vsetvl_e16m8(len);
    vuint16m8_t v1 = __riscv_vle16_v_u16m8((const uint16_t *)src, vl);
    vuint16m8_t v0 = __riscv_vslide1up_vx_u16m8(v1, last, vl);

    vbool2_t surhi = __riscv_vmseq_vx_u16m8_b2(
        __riscv_vand_vx_u16m8(v0, mask, vl), hi_surrogate, vl);
    vbool2_t surlo = __riscv_vmseq_vx_u16m8_b2(
        __riscv_vand_vx_u16m8(v1, mask, vl), lo_surrogate, vl);

    long idx = __riscv_vfirst_m_b2(__riscv_vmxor_mm_b2(surhi, surlo, vl), vl);
    if (idx >= 0) {
      last = simdutf_byteflip<bflip>(idx > 0 ? src[idx - 1] : last);
      return result(error_code::SURROGATE,
                    src - beg + idx - (last - 0xD800u < 0x400u));
      break;
    }
  }
  if (simdutf_byteflip<bflip>(last) - 0xD800u < 0x400u) {
    return result(error_code::SURROGATE,
                  src - beg - 1); /* end on high surrogate */
  } else {
    return result(error_code::SUCCESS, src - beg);
  }
}
#endif // SIMDUTF_FEATURE_UTF16 || SIMDUTF_FEATURE_DETECT_ENCODING

#if SIMDUTF_FEATURE_UTF16 || SIMDUTF_FEATURE_DETECT_ENCODING
simdutf_warn_unused bool
implementation::validate_utf16le(const char16_t *src,
                                 size_t len) const noexcept {
  return rvv_validate_utf16_with_errors<simdutf_ByteFlip::NONE>(src, len)
             .error == error_code::SUCCESS;
}
#endif // SIMDUTF_FEATURE_UTF16 || SIMDUTF_FEATURE_DETECT_ENCODING

#if SIMDUTF_FEATURE_UTF16
simdutf_warn_unused bool
implementation::validate_utf16be(const char16_t *src,
                                 size_t len) const noexcept {
  return validate_utf16be_with_errors(src, len).error == error_code::SUCCESS;
}
#endif // SIMDUTF_FEATURE_UTF16

#if SIMDUTF_FEATURE_UTF16
simdutf_warn_unused result implementation::validate_utf16le_with_errors(
    const char16_t *src, size_t len) const noexcept {
  return rvv_validate_utf16_with_errors<simdutf_ByteFlip::NONE>(src, len);
}

simdutf_warn_unused result implementation::validate_utf16be_with_errors(
    const char16_t *src, size_t len) const noexcept {
  if (supports_zvbb())
    return rvv_validate_utf16_with_errors<simdutf_ByteFlip::ZVBB>(src, len);
  else
    return rvv_validate_utf16_with_errors<simdutf_ByteFlip::V>(src, len);
}
#endif // SIMDUTF_FEATURE_UTF16

#if SIMDUTF_FEATURE_UTF32 || SIMDUTF_FEATURE_DETECT_ENCODING
simdutf_warn_unused bool
implementation::validate_utf32(const char32_t *src, size_t len) const noexcept {
  size_t vlmax = __riscv_vsetvlmax_e32m8();
  vuint32m8_t max = __riscv_vmv_v_x_u32m8(0x10FFFF, vlmax);
  vuint32m8_t maxOff = __riscv_vmv_v_x_u32m8(0xFFFFF7FF, vlmax);
  for (size_t vl; len > 0; len -= vl, src += vl) {
    vl = __riscv_vsetvl_e32m8(len);
    vuint32m8_t v = __riscv_vle32_v_u32m8((uint32_t *)src, vl);
    vuint32m8_t off = __riscv_vadd_vx_u32m8(v, 0xFFFF2000, vl);
    max = __riscv_vmaxu_vv_u32m8_tu(max, max, v, vl);
    maxOff = __riscv_vmaxu_vv_u32m8_tu(maxOff, maxOff, off, vl);
  }
  return __riscv_vfirst_m_b4(
             __riscv_vmor_mm_b4(
                 __riscv_vmsne_vx_u32m8_b4(max, 0x10FFFF, vlmax),
                 __riscv_vmsne_vx_u32m8_b4(maxOff, 0xFFFFF7FF, vlmax), vlmax),
             vlmax) < 0;
}
#endif // SIMDUTF_FEATURE_UTF32 || SIMDUTF_FEATURE_DETECT_ENCODING

#if SIMDUTF_FEATURE_UTF32
simdutf_warn_unused result implementation::validate_utf32_with_errors(
    const char32_t *src, size_t len) const noexcept {
  const char32_t *beg = src;
  for (size_t vl; len > 0; len -= vl, src += vl) {
    vl = __riscv_vsetvl_e32m8(len);
    vuint32m8_t v = __riscv_vle32_v_u32m8((uint32_t *)src, vl);
    vuint32m8_t off = __riscv_vadd_vx_u32m8(v, 0xFFFF2000, vl);
    long idx1 =
        __riscv_vfirst_m_b4(__riscv_vmsgtu_vx_u32m8_b4(v, 0x10FFFF, vl), vl);
    long idx2 = __riscv_vfirst_m_b4(
        __riscv_vmsgtu_vx_u32m8_b4(off, 0xFFFFF7FF, vl), vl);
    if (idx1 >= 0 && idx2 >= 0) {
      if (idx1 <= idx2) {
        return result(error_code::TOO_LARGE, src - beg + idx1);
      } else {
        return result(error_code::SURROGATE, src - beg + idx2);
      }
    }
    if (idx1 >= 0) {
      return result(error_code::TOO_LARGE, src - beg + idx1);
    }
    if (idx2 >= 0) {
      return result(error_code::SURROGATE, src - beg + idx2);
    }
  }
  return result(error_code::SUCCESS, src - beg);
}
#endif // SIMDUTF_FEATURE_UTF32
