ENCCVT
enccvt (encodings converter) - небольшая кроссплатформенная библиотека упрощающая работу с кодировками в C и C++
В данной библиотеке поддерживаются следующие кодировки:
- ASCII (7-битная, с кодовыми страницами не разбирался, возможно займусь в будущем)
 - UTF-8
 - UTF-16LE
 - UTF-16BE
 - UTF-32LE
 - UTF-32BE
 
Все кодировки поддерживаются не смотря на порядок байт текущей пдатформы.
Для C реализованы основные методы для конвертации между кодировками. Для C++ имеется реализация псевдонимов для всех символов с явно определенной кодировкой, адаптированны контейнеры std::basic_string и std::basic_string_view для работы с ними и определены пользовательские литералы для явного указания кодировки строк.
Сборка
Для сборки необходимы:
CMake >= 3.5Clang/GCC/MSVC >= C++20
В папке с проектом выполните следующий набор команд:
mkdir build
cd build
cmake ..
cmake --build .
Краткое описание
C API
Все определения для языка C расположены в ./include/enccvt.h
В C API определены следующие перечисления:
// Перечисления для удобной работы с порядком байт
enum endian_t {
  little_endian = 0,
  big_endian = 1,
  invalid = -1,
  native = /*в зависимости от порядка байт текущей платформы little_endian или big_endian*/,
  net = big_endian,
  host = native
};
// Поддерживаемые кодировки без указания порядка байт
enum encoding_base_t {
  invalid_encoding = -1,
  acii = 0,
  utf_8 = 1,
  utf_16 = 2,
  utf_32 = 4,
  wide = sizeof(wchar_t) == size_t(utf_16) ? utf_16 : // Windows
         sizeof(wchar_t) == size_t(utf_32) ? utf_32 : // *nix
         invalid_encoding                             // \_(0_0)_/
};
static_assert(sizeof(wchar_t) == 2 || sizeof(wchar_t) == 4);
// Кодировки с указание порядка байт
enum encoding_t {
  ASCII   = int(acii) << 1,
  UTF8    = int(utf_8) << 1,
  UTF16LE = int(little_endian)  | (int(utf_16) << 1),
  UTF16BE = int(big_endian)     | (int(utf_16) << 1),
  UTF16   = int(native)         | (int(utf_16) << 1),
  UTF32LE = int(little_endian)  | (int(utf_32) << 1),
  UTF32BE = int(big_endian)     | (int(utf_32) << 1),
  UTF32   = int(native)         | (int(utf_32) << 1),
  WIDELE  = int(little_endian)  | (int(wide) << 1),
  WIDEBE  = int(big_endian)     | (int(wide) << 1),
  WIDE    = int(native)         | (int(wide) << 1)
};
В C API определены следующие функции:
ОБРАЩАЮ ВНИМАНИЕ что все размеры в функции передаются исключительно В БАЙТАХ, а не в символах!
// Получить кодовую позицию из символа ch в кодировке enc (по сути возвращяет символ UTF-32 с порядком байт текущей
// платформы)
char32_t getCodePoint(const void* ch, encoding_t enc);
// Получить размер символа ch в кодировке enc в байтах
size_t getCharSize(const void* ch, encoding_t enc);
// Примечание: работает для любых кодировок, но имеет смысл только для тех что имеют динамический размер символа, то
// есть UTF8, UTF16LE и UTF16BE
// Получить символ предшествующий ch в кодировке enc
void* getPrevChar(void* ch, encoding_t enc);
// Примечание: работает для любых кодировок, но имеет смысл только для тех что имеют динамический размер символа, то
// есть UTF8, UTF16LE и UTF16BE
// Получить символ следующий за ch в кодировке enc (aka (uint8_t*)ch + getCharSize(ch, enc))
void* getNextChar(void* ch, encoding_t enc);
// Примечание: работает для любых кодировок, но имеет смысл только для тех что имеют динамический размер символа, то
// есть UTF8, UTF16LE и UTF16BE
// Определить сколько нужно байт для символа кодовой позиции code_point в кодировке enc
size_t getRequiredBytes(char32_t code_point, encoding_t enc);
// Примечание: работает для любых кодировок, но имеет смысл только для тех что имеют динамический размер символа, то
// есть UTF8, UTF16LE и UTF16BE
// Закодировать кодовую позицию code_point в кодировку enc по адресу to
// Возврвщаемое значение - длинна результирующей символа в байтах
size_t encodeChar(char32_t code_point, void* to, encoding_t enc);
// Замечания:
// Предполагается что по адресу to достаточно памяти для размещения симовла в кодировке enc, рекомендуется
// предварительно проверять функцией getRequiredBytes
// Определить сколько нужно байт для конвертации в кодировку dst_enc строки расположенной по адресу src с размером
// src_byte_size и кодировкой src_enc
size_t getRequiredSizeForEncoding(encoding_t dst_enc, const void* src, size_t src_byte_size, encoding_t src_enc);
// Конвертировать строку расположенную по адресу src с размером src_byte_size и кодировкой src_enc в строку
// расположенную по адресу dst с размером dst_byte_size и кодировкой dst_enc
// Возврвщаемое значение - длинна результирующей строки в байтах
size_t reencode(void* dst, size_t dst_byte_size, encoding_t dst_enc, const void* src, size_t src_byte_size, encoding_t src_enc);
// Замечания:
// Функция ```reencode``` НЕ СТАВИТ символ конца строки '\0'
// Если буфер будет меньше чем необходимый для полного размещения строки в целевой кодировке, функция ```reencode```
// перекодирует столько символов сколько возможно будет разместить полностью
Пример использования:
#include "enccvt/include/enccvt.h"
int main(int, char*[]) {
  // Исходная строка и её размер
  const char* src_str = "Hello мир";
  size_t src_byte_size = strlen(src_str);
  
  // Конвертация в широкую строку
  size_t dst_byte_size = getRequiredSizeForEncoding(WIDE, src_str, src_byte_size, ASCII);
  wchar_t* dst_str = (wchar_t)malloc(dst_byte_size + sizeof(wchar_t));
  reencode(dst_str, dst_byte_size, WIDE, src_str, src_byte_size, ASCII);
  dst_str[dst_byte_size/sizeof(wchar_t)] = L'\0';
  // Проверка результата
  wprintf(L"%s", dst_str);
  
  return EXIT_SUCCESS;
}
C++ API
Все объявления для языка C++ расположены в заголовочном файле ./include/enccvt.hxx и внутри пространств имён enccvt и enccvt::literals.
В C++ API доступны все те же перечисления и функции что и в C API но с определёнными изменениями. В отличии от API для C перечисления в C++ объявлены через enum class, а так же все определения доступные в C на ровне с определениями доступными только в C++ расположены в пространстве имён enccvt.
В API для C++ определены следующие концепты, перечисления, классы, структуры и псевдонимы:
namespace enccvt {
// Аналог std::type_traits определённый ввиду того что std::type_traits не позволяет работать с произвольными типами (по крайней мере на это активно ругается MSVC), лучше
// пристраховатья на будущее и определить аналог, которому будет без разницы с чем работать
template <class char_t>
struct non_standart_char_traits_t;
// Типы символов с определением порядка байт (в данном пример для le-платформы)
using utf16le_char_t = char16_t;         // или enum class utf16le_char_t : char16_t {};
enum class utf16be_char_t : char16_t {}; // или using utf16be_char_t = char16_t;
using wle_char_t = wchar_t;              // или enum class wle_char_t : wchar_t {};
enum class wbe_char_t : wchar_t {};      // или using wbe_char_t = wchar_t;
using utf32le_char_t = char32_t;         // или enum class utf32le_char_t : char32_t {};
enum class utf32be_char_t : char32_t {}; // или using utf32be_char_t = char32_t;
// Черты символов  с определением порядка байт (в данном пример для le-платформы)
using utf16le_char_traits_t = std::char_traits<utf16le_char_t>;           // или non_standart_char_traits_t<utf16le_char_t>
using utf16be_char_traits_t = non_standart_char_traits_t<utf16be_char_t>; // или std::char_traits<utf16be_char_t>
using wle_char_traits_t = std::char_traits<wle_char_t>;                   // или non_standart_char_traits_t<wle_char_t>;
using wbe_char_traits_t = non_standart_char_traits_t<wbe_char_t>;         // или std::char_traits<wbe_char_t>;
using utf32le_char_traits_t = std::char_traits<utf32le_char_t>;           // или non_standart_char_traits_t<utf32le_char_t>
using utf32be_char_traits_t = non_standart_char_traits_t<utf32be_char_t>; // или std::char_traits<utf32be_char_t>
// Концепт определяющий все поддерживаемые в enccvt символы
template<typename char_t>
concept char_c;
// Шаблон для автоматического выбора корректных черт символов
template<char_c char_t>
using type_traits;
// Кодировка без указания порядка байт по типу символа
template<char_c char_t>
constexpr encoding_base_t base_encoding_of;
// Кодировка по типу символа
template<char_c char_t>
constexpr encoding_t encoding_of;
// Кодировка по кодировке без указания порядка байт и порядку байт
template<encoding_base_t encoding_base, endian_t endian>
constexpr encoding_t to_encoding;
// Тип символа по кодировке
template<encoding_t encoding>
using char_type;
// Строки для всех поддерживаемых в enccvt кодировок
using ascii_string = std::basic_string<char, type_traits<char>, allocator<char>>;
using u8_string = std::basic_string<char8_t, type_traits<char8_t>, allocator<char8_t>>;
using u16le_string = std::basic_string<utf16le_char_t, type_traits<utf16le_char_t>, std::allocator<utf16le_char_t>>;
using u16be_string = std::basic_string<utf16be_char_t, type_traits<utf16be_char_t>, std::allocator<utf16be_char_t>>;
using wle_string = std::basic_string<wle_char_t, type_traits<wle_char_t>, allocator<wle_char_t>>;
using wbe_string = std::basic_string<wbe_char_t, type_traits<wbe_char_t>, allocator<wbe_char_t>>;
using u32le_string = std::basic_string<utf32le_char_t, type_traits<utf32le_char_t>, std::allocator<utf32le_char_t>>;
using u32be_string = std::basic_string<utf32be_char_t, type_traits<utf32be_char_t>, std::allocator<utf32be_char_t>>;
// Не владеющие строки для всех поддерживаемых в enccvt кодировок
using ascii_string_view = std::basic_string_view<char, type_traits<char>>;
using u8_string_view = std::basic_string_view<char8_t, type_traits<char8_t>>;
using u16le_string_view = std::basic_string_view<utf16le_char_t, type_traits<utf16le_char_t>>;
using u16be_string_view = std::basic_string_view<utf16be_char_t, type_traits<utf16be_char_t>>;
using wle_string_view = std::basic_string_view<wle_char_t, type_traits<wle_char_t>>;
using wbe_string_view = std::basic_string_view<wbe_char_t, type_traits<wbe_char_t>>;
using u32le_string_view = std::basic_string_view<utf32le_char_t, type_traits<utf32le_char_t>>;
using u32be_string_view = std::basic_string_view<utf32be_char_t, type_traits<utf32be_char_t>>;
} // namespace enccvt
В API для C++ определены следующие функции и пользовательские литералы:
namespace enccvt {
namespace literals {
// Литералы для явного объявления C-строк с указанным порядком байт
template<detail::u16static_string_t static_string> auto operator ""_le ();
template<detail::wstatic_string_t static_string> auto operator ""_le ();
template<detail::u32static_string_t static_string> auto operator ""_le ();
template<detail::u16static_string_t static_string> auto operator ""_be ();
template<detail::wstatic_string_t static_string> auto operator ""_be ();
template<detail::u32static_string_t static_string> auto operator ""_be ();
// Литералы для явного объявления не владеющий строк с указанным порядком байт
template<detail::u16static_string_t static_string> auto operator ""_le_sv ();
template<detail::wstatic_string_t static_string> auto operator ""_le_sv ();
template<detail::u32static_string_t static_string> auto operator ""_le_sv ();
template<detail::u16static_string_t static_string> auto operator ""_be_sv ();
template<detail::wstatic_string_t static_string> auto operator ""_be_sv ();
template<detail::u32static_string_t static_string> auto operator ""_be_sv ();
// Литералы для явного объявления строк с указанным порядком байт
template<detail::u16static_string_t static_string> auto operator ""_le_s ();
template<detail::wstatic_string_t static_string> auto operator ""_le_s ();
template<detail::u32static_string_t static_string> auto operator ""_le_s ();
template<detail::u16static_string_t static_string> auto operator ""_be_s ();
template<detail::wstatic_string_t static_string> auto operator ""_be_s ();
template<detail::u32static_string_t static_string> auto operator ""_be_s ();
// Версии с явным указанием типа, потому что у компилторов от microsoft с этим проблемы
// P.S. Помогите Даше найти wchar_t...
template<detail::u16static_string_t static_string> auto operator ""_u16_le ();
template<detail::wstatic_string_t static_string> auto operator ""_w_le ();
template<detail::u32static_string_t static_string> auto operator ""_u32_le ();
template<detail::u16static_string_t static_string> auto operator ""_u16_be ();
template<detail::wstatic_string_t static_string> auto operator ""_w_be ();
template<detail::u32static_string_t static_string> auto operator ""_u32_be ();
template<detail::u16static_string_t static_string> auto operator ""_u16_le_sv ();
template<detail::wstatic_string_t static_string> auto operator ""_w_le_sv ();
template<detail::u32static_string_t static_string> auto operator ""_u32_le_sv ();
template<detail::u16static_string_t static_string> auto operator ""_u16_be_sv ();
template<detail::wstatic_string_t static_string> auto operator ""_w_be_sv ();
template<detail::u32static_string_t static_string> auto operator ""_u32_be_sv ();
template<detail::u16static_string_t static_string> auto operator ""_u16_le_s ();
template<detail::wstatic_string_t static_string> auto operator ""_w_le_s ();
template<detail::u32static_string_t static_string> auto operator ""_u32_le_s ();
template<detail::u16static_string_t static_string> auto operator ""_u16_be_s ();
template<detail::wstatic_string_t static_string> auto operator ""_w_be_s ();
template<detail::u32static_string_t static_string> auto operator ""_u32_be_s ();
} // namespace literals
// Определить кодировку без порядка байт по типу символа
template<str::char_c char_t>
constexpr encoding_base_t base_encoding_of;
// Определить кодировку с порядком байт по типу символа
template<str::char_c char_t>
constexpr encoding_t encoding_of;
// Конвертировать строку src из строки std::basic_string_view<src_char_t, str::type_traits<src_char_t>> в строку std::basic_string<dst_char_t, str::type_traits<dst_char_t>>
template<str::char_c dst_char_t, str::char_c src_char_t, template<typename> typename allocator = std::allocator>
std::basic_string<dst_char_t, str::type_traits<dst_char_t>, allocator<dst_char_t>> reencodeStringView(const std::basic_string_view<src_char_t, str::type_traits<src_char_t>>& src);
// Конвертировать строку src из строки std::basic_string<src_char_t, str::type_traits<src_char_t>> в строку std::basic_string<dst_char_t, str::type_traits<dst_char_t>>
template<str::char_c dst_char_t, str::char_c src_char_t, template<typename> typename dst_allocator = std::allocator, template<typename> typename src_allocator = dst_allocator>
std::basic_string<dst_char_t, str::type_traits<dst_char_t>, dst_allocator<dst_char_t>> reencodeString(const std::basic_string<src_char_t, str::type_traits<src_char_t>, src_allocator<src_char_t>>& src);
} // namespace enccvt
Пример использования:
#include "enccvt/include/enccvt.hxx"
int main(int, char* []) try {
  using namespace enccvt::literals;
  // объявления с использованияем литералов с явным указанием кодировки
  enccvt::u16le_string_view u16le = u"Привет world"_u16_le_sv;
  enccvt::u16be_string_view u16be = u"Привет world"_u16_be_sv;
  enccvt::wle_string_view wle = L"Привет world"_w_le_sv;
  enccvt::wbe_string_view wbe = L"Привет world"_w_be_sv;
  enccvt::u32le_string_view u32le = U"Привет world"_u32_le_sv;
  enccvt::u32be_string_view u32be = U"Привет world"_u32_be_sv;
  // Конвертация строк
  auto u16le_cvt_result = enccvt::reencodeStringView<enccvt::utf16le_char_t>(u16be);
  auto u16be_cvt_result = enccvt::reencodeStringView<enccvt::utf16be_char_t>(u16le);
  auto wle_cvt_result = enccvt::reencodeStringView<enccvt::wle_char_t>(wbe);
  auto wbe_cvt_result = enccvt::reencodeStringView<enccvt::wbe_char_t>(wle);
  auto u32le_cvt_result = enccvt::reencodeStringView<enccvt::utf32le_char_t>(u32be);
  auto u32be_cvt_result = enccvt::reencodeStringView<enccvt::utf32be_char_t>(u32le);
  // Проверка результатов конвертации
  std::cout
      << "u16le_cvt_result == u16le -> " << std::boolalpha << bool(u16le_cvt_result == u16le) << std::endl
      << "u16be_cvt_result == u16be -> " << std::boolalpha << bool(u16be_cvt_result == u16be) << std::endl
      << "wle_cvt_result == wle -> " << std::boolalpha << bool(wle_cvt_result == wle) << std::endl
      << "wbe_cvt_result == wbe -> " << std::boolalpha << bool(wbe_cvt_result == wbe) << std::endl
      << "u32le_cvt_result == u32le -> " << std::boolalpha << bool(u32le_cvt_result == u32le) << std::endl
      << "u32be_cvt_result == u32be -> " << std::boolalpha << bool(u32be_cvt_result == u32be) << std::endl;
  return EXIT_SUCCESS;
} catch (std::exception& e) {
  std::cerr << e.what() << std::endl;
  return EXIT_FAILURE;
} catch(...) {
  std::cerr << "Unhandled exception!" << std::endl;
  return EXIT_FAILURE;
}