summaryrefslogtreecommitdiffstats
path: root/ext/sqlite_modern_cpp/sqlite_modern_cpp/utility/utf16_utf8.h
diff options
context:
space:
mode:
Diffstat (limited to 'ext/sqlite_modern_cpp/sqlite_modern_cpp/utility/utf16_utf8.h')
-rw-r--r--ext/sqlite_modern_cpp/sqlite_modern_cpp/utility/utf16_utf8.h42
1 files changed, 42 insertions, 0 deletions
diff --git a/ext/sqlite_modern_cpp/sqlite_modern_cpp/utility/utf16_utf8.h b/ext/sqlite_modern_cpp/sqlite_modern_cpp/utility/utf16_utf8.h
new file mode 100644
index 0000000..ea21723
--- /dev/null
+++ b/ext/sqlite_modern_cpp/sqlite_modern_cpp/utility/utf16_utf8.h
@@ -0,0 +1,42 @@
+#pragma once
+
+#include <locale>
+#include <string>
+#include <algorithm>
+
+#include "../errors.h"
+
+namespace sqlite {
+ namespace utility {
+ inline std::string utf16_to_utf8(const std::u16string &input) {
+ struct : std::codecvt<char16_t, char, std::mbstate_t> {
+ } codecvt;
+ std::mbstate_t state{};
+ std::string result((std::max)(input.size() * 3 / 2, std::size_t(4)), '\0');
+ const char16_t *remaining_input = input.data();
+ std::size_t produced_output = 0;
+ while(true) {
+ char *used_output;
+ switch(codecvt.out(state, remaining_input, &input[input.size()],
+ remaining_input, &result[produced_output],
+ &result[result.size() - 1] + 1, used_output)) {
+ case std::codecvt_base::ok:
+ result.resize(used_output - result.data());
+ return result;
+ case std::codecvt_base::noconv:
+ // This should be unreachable
+ case std::codecvt_base::error:
+ throw errors::invalid_utf16("Invalid UTF-16 input", "");
+ case std::codecvt_base::partial:
+ if(used_output == result.data() + produced_output)
+ throw errors::invalid_utf16("Unexpected end of input", "");
+ produced_output = used_output - result.data();
+ result.resize(
+ result.size()
+ + (std::max)((&input[input.size()] - remaining_input) * 3 / 2,
+ std::ptrdiff_t(4)));
+ }
+ }
+ }
+ } // namespace utility
+} // namespace sqlite