diff options
| author | Can Celik <ogulcancelik@gmail.com> | 2026-04-04 19:22:04 +0300 |
|---|---|---|
| committer | Can Celik <ogulcancelik@gmail.com> | 2026-04-05 01:23:06 +0300 |
| commit | 41dd733ca7123c9ac4bfc6c781697a660e95b052 (patch) | |
| tree | 3fc91815ca139ef2e0b281bba0abda4ef15175f4 /vendor/libghostty-vt/src/simd/vt.cpp | |
| parent | 55c138263e190a1e79d4b997bfb9397306c0ec1f (diff) | |
feat: finish ghostty pane backend integration
Diffstat (limited to 'vendor/libghostty-vt/src/simd/vt.cpp')
| -rw-r--r-- | vendor/libghostty-vt/src/simd/vt.cpp | 169 |
1 files changed, 169 insertions, 0 deletions
diff --git a/vendor/libghostty-vt/src/simd/vt.cpp b/vendor/libghostty-vt/src/simd/vt.cpp new file mode 100644 index 00000000..d8133abf --- /dev/null +++ b/vendor/libghostty-vt/src/simd/vt.cpp @@ -0,0 +1,169 @@ +// Generates code for every target that this compiler can support. +#undef HWY_TARGET_INCLUDE +#define HWY_TARGET_INCLUDE "simd/vt.cpp" // this file +#include <hwy/foreach_target.h> // must come before highway.h +#include <hwy/highway.h> + +#include <simdutf.h> +#include <utf8.h> +#include <vector> + +#include <simd/index_of.h> +#include <simd/vt.h> + +HWY_BEFORE_NAMESPACE(); +namespace ghostty { +namespace HWY_NAMESPACE { + +namespace hn = hwy::HWY_NAMESPACE; + +using T = uint8_t; + +// Decode the UTF-8 text in input into output. Returns the number of decoded +// characters. This function assumes output is large enough. +// +// This function handles malformed UTF-8 sequences by inserting a +// replacement character (U+FFFD) and continuing to decode. This function +// will consume the entire input no matter what. +size_t DecodeUTF8(const uint8_t* HWY_RESTRICT input, + size_t count, + char32_t* output) { + // Its possible for our input to be empty since DecodeUTF8UntilControlSeq + // doesn't check for this. + if (count == 0) { + return 0; + } + + // Assume no errors for fast path. + const size_t decoded = simdutf::convert_utf8_to_utf32( + reinterpret_cast<const char*>(input), count, output); + if (decoded > 0) { + return decoded; + } + + // Errors in the UTF input, take a slow path and do a decode with + // replacement (with U+FFFD). Note that simdutf doesn't have a + // decode with replacement API: + // https://github.com/simdutf/simdutf/issues/147 + // + // Because of this, we use a separate library with heap allocation + // that is much, much slower (the allocation is slower, the algorithm + // is slower, etc.) This is just so we have something that works. + // I want to replace this. + std::vector<char> replacement_result; + utf8::replace_invalid(input, input + count, + std::back_inserter(replacement_result), 0xFFFD); + return DecodeUTF8(reinterpret_cast<const uint8_t*>(replacement_result.data()), + replacement_result.size(), output); +} + +/// Decode the UTF-8 text in input into output until an escape +/// character is found. This returns the number of bytes consumed +/// from input and writes the number of decoded characters into +/// output_count. +/// +/// This may return a value less than count even with no escape +/// character if the input ends with an incomplete UTF-8 sequence. +/// The caller should check the next byte manually to determine +/// if it is incomplete. +template <class D> +size_t DecodeUTF8UntilControlSeqImpl(D d, + const T* HWY_RESTRICT input, + size_t count, + char32_t* output, + size_t* output_count) { + const size_t N = hn::Lanes(d); + + // Create a vector containing ESC since that denotes a control sequence. + const hn::Vec<D> esc_vec = Set(d, 0x1B); + + // Compare N elements at a time. + size_t i = 0; + for (; i + N <= count; i += N) { + // Load the N elements from our input into a vector. + const hn::Vec<D> input_vec = hn::LoadU(d, input + i); + + // If we don't have any escapes we keep going. We want to accumulate + // the largest possible valid UTF-8 sequence before decoding. + // TODO(mitchellh): benchmark this vs decoding every time + const auto esc_idx = IndexOfChunk(d, esc_vec, input_vec); + if (!esc_idx) { + continue; + } + + // We have an ESC char, decode up to this point. We start by assuming + // a valid UTF-8 sequence and slow-path into error handling if we find + // an invalid sequence. + *output_count = DecodeUTF8(input, i + esc_idx.value(), output); + return i + esc_idx.value(); + } + + // If we have leftover input then we decode it one byte at a time (slow!) + // using pretty much the same logic as above. + if (i != count) { + const hn::CappedTag<T, 1> d1; + using D1 = decltype(d1); + const hn::Vec<D1> esc1 = Set(d1, hn::GetLane(esc_vec)); + for (; i < count; ++i) { + const hn::Vec<D1> input_vec = hn::LoadU(d1, input + i); + const auto esc_idx = IndexOfChunk(d1, esc1, input_vec); + if (!esc_idx) { + continue; + } + + *output_count = DecodeUTF8(input, i + esc_idx.value(), output); + return i + esc_idx.value(); + } + } + + // If we reached this point, its possible for our input to have an + // incomplete sequence because we're consuming the full input. We need + // to trim any incomplete sequences from the end of the input. + const size_t trimmed_len = + simdutf::trim_partial_utf8(reinterpret_cast<const char*>(input), i); + *output_count = DecodeUTF8(input, trimmed_len, output); + return trimmed_len; +} + +size_t DecodeUTF8UntilControlSeq(const uint8_t* HWY_RESTRICT input, + size_t count, + char32_t* output, + size_t* output_count) { + const hn::ScalableTag<uint8_t> d; + return DecodeUTF8UntilControlSeqImpl(d, input, count, output, output_count); +} + +} // namespace HWY_NAMESPACE +} // namespace ghostty +HWY_AFTER_NAMESPACE(); + +// HWY_ONCE is true for only one of the target passes +#if HWY_ONCE + +namespace ghostty { + +HWY_EXPORT(DecodeUTF8UntilControlSeq); + +size_t DecodeUTF8UntilControlSeq(const uint8_t* HWY_RESTRICT input, + size_t count, + char32_t* output, + size_t* output_count) { + return HWY_DYNAMIC_DISPATCH(DecodeUTF8UntilControlSeq)(input, count, output, + output_count); +} + +} // namespace ghostty + +extern "C" { + +size_t ghostty_simd_decode_utf8_until_control_seq(const uint8_t* HWY_RESTRICT + input, + size_t count, + char32_t* output, + size_t* output_count) { + return ghostty::DecodeUTF8UntilControlSeq(input, count, output, output_count); +} + +} // extern "C" + +#endif // HWY_ONCE |
