text: #pragma once
  
  #include <string>
  #include <wchar.h>
  
  #include <algorithm>
  #include <memory>
  
  #pragma warning(push, 0)
      #include <utf8/utf8.h>
      #ifdef WIN32
          #include <wcwidth.h>
      #endif
  #pragma warning(pop)
  
  #ifdef max
  #undef max
  #endif
  
  inline std::wstring u8to16(const std::string& u8) {
      std::wstring result;
      utf8::utf8to16(u8.begin(), u8.end(), std::back_inserter(result));
      return result;
  }
  
  inline std::string u16to8(const std::wstring& u16) {
      std::string result;
      utf8::utf16to8(u16.begin(), u16.end(), std::back_inserter(result));
      return result;
  }
  
  static inline size_t u8cols(const std::string& str) {
      std::wstring wstr = u8to16(str);
  #ifdef WIN32
      int result = std::max(0, mk_wcswidth(wstr.c_str(), wstr.size()));
  #else
      int result = std::max(0, wcswidth(wstr.c_str(), wstr.size()));
  #endif
      return (result > 0) ? result : str.size();
  }
  
  inline static size_t u8len(const std::string& str) {
      try {
          return utf8::distance(str.begin(), str.end());
      }
      catch (...) {
          return str.length();
      }
  }
  
  /* get the (raw) character index of the "nth" logical/display character */
  inline static size_t u8offset(const std::string& str, int n) {
      if (str.size() == 0) {
          return std::string::npos;
      }
  
      std::string::const_iterator it = str.begin();
  
      int count = 0;
      while (count < n && it != str.end()) {
          utf8::unchecked::next(it);
          ++count;
      }
  
      return (size_t)(it - str.begin());
  }
  
  inline static std::string u8substr(const std::string& in, int offset, int len) {
      std::string::const_iterator begin = in.begin() + offset;
      std::string::const_iterator it = begin;
  
      int count = 0;
      while (count < len && it != in.end()) {
          utf8::unchecked::next(it);
          ++count;
      }
  
      return std::string(begin, it);
  }
  
  template<typename... Args>
  static std::string u8fmt(const std::string& format, Args ... args) {
      /* https://stackoverflow.com/a/26221725 */
      size_t size = std::snprintf(nullptr, 0, format.c_str(), args ...) + 1; /* extra space for '\0' */
      std::unique_ptr<char[]> buf(new char[size]);
      std::snprintf(buf.get(), size, format.c_str(), args ...);
      return std::string(buf.get(), buf.get() + size - 1); /* omit the '\0' */
  }
  
  static inline void u8replace(
      std::string& input, const std::string& find, const std::string& replace)
  {
      size_t pos = input.find(find);
      while (pos != std::string::npos) {
          input.replace(pos, find.size(), replace);
          pos = input.find(find, pos + replace.size());
      }
  }
  