1//
2// Copyright (c) 2009-2011 Artyom Beilis (Tonkikh)
3// Copyright (c) 2022-2023 Alexander Grund
4//
5// Distributed under the Boost Software License, Version 1.0.
6// https://www.boost.org/LICENSE_1_0.txt
7
8#include "iconv_codecvt.hpp"
9#include <boost/assert.hpp>
10#include <array>
11#include <cerrno>
12#include <limits>
13#include <vector>
14#ifdef BOOST_LOCALE_WITH_ICONV
15# include "boost/locale/util/encoding.hpp"
16# include "boost/locale/util/iconv.hpp"
17# include "boost/locale/util/make_std_unique.hpp"
18#endif
19
20namespace boost { namespace locale {
21
22#ifdef BOOST_LOCALE_WITH_ICONV
23 class mb2_iconv_converter : public util::base_converter {
24 public:
25 mb2_iconv_converter(const std::string& encoding) : encoding_(encoding)
26 {
27 iconv_handle d(iconv_open(tocode: util::utf_name<uint32_t>(), fromcode: encoding.c_str()));
28 if(!d)
29 throw std::runtime_error("Unsupported encoding" + encoding);
30
31 for(unsigned c = 0; c < first_byte_table_.size(); c++) {
32 const char ibuf[2] = {char(c), 0};
33 size_t insize = sizeof(ibuf);
34 uint32_t obuf[2] = {illegal, illegal};
35 size_t outsize = sizeof(obuf);
36 // Basic single codepoint conversion
37 call_iconv(d, in: ibuf, insize: &insize, out: reinterpret_cast<char*>(obuf), outsize: &outsize);
38 if(insize == 0 && outsize == 0 && obuf[1] == 0)
39 first_byte_table_[c] = obuf[0];
40 else {
41 // Test if this is illegal first byte or incomplete
42 insize = 1;
43 outsize = sizeof(obuf);
44 call_iconv(d, nullptr, nullptr, nullptr, nullptr);
45 const size_t res = call_iconv(d, in: ibuf, insize: &insize, out: reinterpret_cast<char*>(obuf), outsize: &outsize);
46
47 // Now if this single byte starts a sequence we add incomplete
48 // to know to ask that we need two bytes, otherwise it may only be illegal
49
50 first_byte_table_[c] = (res == size_t(-1) && errno == EINVAL) ? incomplete : illegal;
51 }
52 }
53 }
54
55 mb2_iconv_converter(const mb2_iconv_converter& other) :
56 first_byte_table_(other.first_byte_table_), encoding_(other.encoding_)
57 {}
58
59 bool is_thread_safe() const override { return false; }
60
61 mb2_iconv_converter* clone() const override { return new mb2_iconv_converter(*this); }
62
63 utf::code_point to_unicode(const char*& begin, const char* end) override
64 {
65 if(begin == end)
66 return incomplete;
67
68 const unsigned char seq0 = *begin;
69
70# if defined(BOOST_GCC_VERSION) && BOOST_GCC_VERSION >= 40600
71# pragma GCC diagnostic push
72# pragma GCC diagnostic ignored "-Wtype-limits"
73# endif
74 static_assert(std::numeric_limits<unsigned char>::max()
75 < std::tuple_size<decltype(first_byte_table_)>::value,
76 "Wrong table size");
77# if defined(BOOST_GCC_VERSION) && BOOST_GCC_VERSION >= 40600
78# pragma GCC diagnostic pop
79# endif
80
81 const uint32_t index = first_byte_table_[seq0];
82 if(index == illegal)
83 return illegal;
84 if(index != incomplete) {
85 begin++;
86 return index;
87 } else if(begin + 1 == end)
88 return incomplete;
89
90 open(d&: to_utf_, to: util::utf_name<uint32_t>(), from: encoding_.c_str());
91
92 // maybe illegal or may be double byte
93
94 const char inseq[3] = {static_cast<char>(seq0), begin[1], 0};
95 size_t insize = sizeof(inseq);
96 uint32_t result[2] = {illegal, illegal};
97 size_t outsize = sizeof(result);
98 call_iconv(d: to_utf_, in: inseq, insize: &insize, out: reinterpret_cast<char*>(result), outsize: &outsize);
99 if(outsize == 0 && insize == 0 && result[1] == 0) {
100 begin += 2;
101 return result[0];
102 }
103 return illegal;
104 }
105
106 utf::len_or_error from_unicode(utf::code_point cp, char* begin, const char* end) override
107 {
108 if(cp == 0) {
109 if(begin != end) {
110 *begin = 0;
111 return 1;
112 } else
113 return incomplete;
114 }
115
116 open(d&: from_utf_, to: encoding_.c_str(), from: util::utf_name<utf::code_point>());
117
118 const utf::code_point inbuf[2] = {cp, 0};
119 size_t insize = sizeof(inbuf);
120 char outseq[3] = {0};
121 size_t outsize = sizeof(outseq);
122
123 call_iconv(d: from_utf_, in: reinterpret_cast<const char*>(inbuf), insize: &insize, out: outseq, outsize: &outsize);
124
125 if(insize != 0 || outsize == sizeof(outseq))
126 return illegal;
127 const size_t len = sizeof(outseq) - outsize - 1; // Skip trailing NULL
128 if(static_cast<std::ptrdiff_t>(len) > end - begin)
129 return incomplete;
130 for(unsigned i = 0; i < len; i++)
131 *begin++ = outseq[i];
132 return static_cast<utf::code_point>(len);
133 }
134
135 int max_len() const override
136 {
137 return 2;
138 }
139
140 private:
141 std::array<uint32_t, 256> first_byte_table_;
142 std::string encoding_;
143 iconv_handle to_utf_, from_utf_;
144
145 static void open(iconv_handle& d, const char* to, const char* from)
146 {
147 if(!d)
148 d = iconv_open(tocode: to, fromcode: from);
149 BOOST_ASSERT(d);
150 }
151 };
152
153 std::unique_ptr<util::base_converter> create_iconv_converter(const std::string& encoding)
154 {
155 try {
156 return make_std_unique<mb2_iconv_converter>(args: encoding);
157 } catch(const std::exception&) {
158 return nullptr;
159 }
160 }
161
162#else // no iconv
163 std::unique_ptr<util::base_converter> create_iconv_converter(const std::string& /*encoding*/)
164 {
165 return nullptr;
166 }
167#endif
168
169}} // namespace boost::locale
170

source code of boost/libs/locale/src/boost/locale/shared/iconv_codecvt.cpp