Botan 3.13.0
Crypto and TLS for C&
charset.cpp
Go to the documentation of this file.
1/*
2* Character Set Handling
3* (C) 1999-2007,2021 Jack Lloyd
4*
5* Botan is released under the Simplified BSD License (see license.txt)
6*/
7
8#include <botan/internal/charset.h>
9
10#include <botan/exceptn.h>
11#include <botan/internal/loadstor.h>
12
13namespace Botan {
14
15namespace {
16
17void append_utf8_for(std::string& s, uint32_t c) {
18 if(c >= 0xD800 && c < 0xE000) {
19 throw Decoding_Error("Invalid Unicode character");
20 }
21
22 if(c <= 0x7F) {
23 const uint8_t b0 = static_cast<uint8_t>(c);
24 s.push_back(static_cast<char>(b0));
25 } else if(c <= 0x7FF) {
26 const uint8_t b0 = 0xC0 | static_cast<uint8_t>(c >> 6);
27 const uint8_t b1 = 0x80 | static_cast<uint8_t>(c & 0x3F);
28 s.push_back(static_cast<char>(b0));
29 s.push_back(static_cast<char>(b1));
30 } else if(c <= 0xFFFF) {
31 const uint8_t b0 = 0xE0 | static_cast<uint8_t>(c >> 12);
32 const uint8_t b1 = 0x80 | static_cast<uint8_t>((c >> 6) & 0x3F);
33 const uint8_t b2 = 0x80 | static_cast<uint8_t>(c & 0x3F);
34 s.push_back(static_cast<char>(b0));
35 s.push_back(static_cast<char>(b1));
36 s.push_back(static_cast<char>(b2));
37 } else if(c <= 0x10FFFF) {
38 const uint8_t b0 = 0xF0 | static_cast<uint8_t>(c >> 18);
39 const uint8_t b1 = 0x80 | static_cast<uint8_t>((c >> 12) & 0x3F);
40 const uint8_t b2 = 0x80 | static_cast<uint8_t>((c >> 6) & 0x3F);
41 const uint8_t b3 = 0x80 | static_cast<uint8_t>(c & 0x3F);
42 s.push_back(static_cast<char>(b0));
43 s.push_back(static_cast<char>(b1));
44 s.push_back(static_cast<char>(b2));
45 s.push_back(static_cast<char>(b3));
46 } else {
47 throw Decoding_Error("Invalid Unicode character");
48 }
49}
50
51} // namespace
52
53uint32_t next_utf8_codepoint(std::string_view utf8, size_t& pos) {
54 auto read_continuation = [&]() -> uint32_t {
55 if(pos >= utf8.size()) {
56 throw Decoding_Error("Invalid UTF-8 sequence");
57 }
58 const uint8_t b = static_cast<uint8_t>(utf8[pos++]);
59 if((b & 0xC0) != 0x80) {
60 throw Decoding_Error("Invalid UTF-8 sequence");
61 }
62 return b & 0x3F;
63 };
64
65 if(pos >= utf8.size()) {
66 throw Decoding_Error("Invalid UTF-8 sequence");
67 }
68 const uint8_t lead = static_cast<uint8_t>(utf8[pos++]);
69 uint32_t c = 0;
70
71 if(lead <= 0x7F) {
72 c = lead;
73 } else if((lead & 0xE0) == 0xC0) {
74 c = (lead & 0x1F) << 6;
75 c |= read_continuation();
76 if(c < 0x80) {
77 throw Decoding_Error("Overlong UTF-8 sequence");
78 }
79 } else if((lead & 0xF0) == 0xE0) {
80 c = (lead & 0x0F) << 12;
81 c |= read_continuation() << 6;
82 c |= read_continuation();
83 if(c < 0x800) {
84 throw Decoding_Error("Overlong UTF-8 sequence");
85 }
86 } else if((lead & 0xF8) == 0xF0) {
87 c = (lead & 0x07) << 18;
88 c |= read_continuation() << 12;
89 c |= read_continuation() << 6;
90 c |= read_continuation();
91 if(c < 0x10000) {
92 throw Decoding_Error("Overlong UTF-8 sequence");
93 }
94 } else {
95 throw Decoding_Error("Invalid UTF-8 sequence");
96 }
97
98 if(c > 0x10FFFF) {
99 throw Decoding_Error("UTF-8 sequence encodes value outside Unicode range");
100 }
101 if(c >= 0xD800 && c < 0xE000) {
102 throw Decoding_Error("UTF-8 sequence encodes surrogate code point");
103 }
104
105 return c;
106}
107
108bool is_valid_utf8(std::string_view utf8) {
109 try {
110 size_t pos = 0;
111 while(pos < utf8.size()) {
112 const uint32_t c = next_utf8_codepoint(utf8, pos);
113 BOTAN_UNUSED(c);
114 }
115 } catch(Decoding_Error&) {
116 return false;
117 }
118 return true;
119}
120
121std::string ucs2_to_utf8(std::span<const uint8_t> ucs2) {
122 if(ucs2.size() % 2 != 0) {
123 throw Decoding_Error("Invalid length for UCS-2 string");
124 }
125
126 const size_t chars = ucs2.size() / 2;
127
128 std::string s;
129 for(size_t i = 0; i != chars; ++i) {
130 const uint32_t c = load_be<uint16_t>(ucs2.data(), i);
131 append_utf8_for(s, c);
132 }
133
134 return s;
135}
136
137std::vector<uint8_t> utf8_to_ucs2(std::string_view utf8) {
138 std::vector<uint8_t> out;
139 out.reserve(utf8.size() * 2);
140
141 size_t pos = 0;
142 while(pos < utf8.size()) {
143 const uint32_t c = next_utf8_codepoint(utf8, pos);
144 if(c > 0xFFFF) {
145 throw Decoding_Error("Cannot encode character in UCS-2");
146 }
147 const uint16_t val = static_cast<uint16_t>(c);
148 out.push_back(get_byte<0>(val));
149 out.push_back(get_byte<1>(val));
150 }
151
152 return out;
153}
154
155std::string ucs4_to_utf8(std::span<const uint8_t> ucs4) {
156 if(ucs4.size() % 4 != 0) {
157 throw Decoding_Error("Invalid length for UCS-4 string");
158 }
159
160 const size_t chars = ucs4.size() / 4;
161
162 std::string s;
163 for(size_t i = 0; i != chars; ++i) {
164 const uint32_t c = load_be<uint32_t>(ucs4.data(), i);
165 append_utf8_for(s, c);
166 }
167
168 return s;
169}
170
171std::vector<uint8_t> utf8_to_ucs4(std::string_view utf8) {
172 std::vector<uint8_t> out;
173 out.reserve(utf8.size() * 4);
174
175 size_t pos = 0;
176 while(pos < utf8.size()) {
177 const uint32_t val = next_utf8_codepoint(utf8, pos);
178 out.push_back(get_byte<0>(val));
179 out.push_back(get_byte<1>(val));
180 out.push_back(get_byte<2>(val));
181 out.push_back(get_byte<3>(val));
182 }
183
184 return out;
185}
186
187/*
188* Convert from ISO 8859-1 to UTF-8
189*/
190std::string latin1_to_utf8(std::span<const uint8_t> chars) {
191 std::string s;
192 for(const uint8_t b : chars) {
193 append_utf8_for(s, static_cast<uint32_t>(b));
194 }
195 return s;
196}
197
199 const uint8_t b = static_cast<uint8_t>(c);
200 return b < 0x20 || b == 0x7F;
201}
202
203bool is_unicode_control_char(uint32_t cp) {
204 return cp < 0x20 || (cp >= 0x7F && cp <= 0x9F);
205}
206
207std::string escape_control_chars(std::string_view utf8) {
208 std::string out;
209 out.reserve(utf8.size());
210
211 const auto append_hex_escape = [&](uint8_t b) {
212 out += "\\x";
213 out += nibble_to_hex(b >> 4);
214 out += nibble_to_hex(b);
215 };
216
217 size_t pos = 0;
218 while(pos < utf8.size()) {
219 const size_t start = pos;
220
221 uint32_t cp = 0;
222 try {
223 cp = next_utf8_codepoint(utf8, pos);
224 } catch(const Decoding_Error&) {
225 // Not valid UTF-8: escape the offending byte and resume
226 append_hex_escape(static_cast<uint8_t>(utf8[start]));
227 pos = start + 1;
228 continue;
229 }
230
232 for(size_t i = start; i < pos; ++i) {
233 append_hex_escape(static_cast<uint8_t>(utf8[i]));
234 }
235 } else {
236 out.append(utf8.substr(start, pos - start));
237 }
238 }
239
240 return out;
241}
242
243std::string format_char_for_display(char c) {
244 std::string out;
245 out += '\'';
246
247 if(c == '\t') {
248 out += "\\t";
249 } else if(c == '\n') {
250 out += "\\n";
251 } else if(c == '\r') {
252 out += "\\r";
253 } else if(is_ascii_control_char(c) || static_cast<uint8_t>(c) >= 0x80) {
254 const auto b = static_cast<uint8_t>(c);
255 out += "\\x";
256 out += nibble_to_hex(b >> 4);
257 out += nibble_to_hex(b);
258 } else {
259 out += c;
260 }
261
262 out += '\'';
263
264 return out;
265}
266
267} // namespace Botan
#define BOTAN_UNUSED
Definition assert.h:144
constexpr uint8_t get_byte(T input)
Definition loadstor.h:79
std::string format_char_for_display(char c)
Definition charset.cpp:243
constexpr char nibble_to_hex(uint8_t b)
Definition charset.h:76
bool is_unicode_control_char(uint32_t cp)
Definition charset.cpp:203
bool is_valid_utf8(std::string_view utf8)
Definition charset.cpp:108
std::string latin1_to_utf8(std::span< const uint8_t > chars)
Definition charset.cpp:190
std::vector< uint8_t > utf8_to_ucs4(std::string_view utf8)
Definition charset.cpp:171
bool is_ascii_control_char(char c)
Definition charset.cpp:198
uint32_t next_utf8_codepoint(std::string_view utf8, size_t &pos)
Definition charset.cpp:53
std::string ucs2_to_utf8(std::span< const uint8_t > ucs2)
Definition charset.cpp:121
std::string ucs4_to_utf8(std::span< const uint8_t > ucs4)
Definition charset.cpp:155
std::string escape_control_chars(std::string_view utf8)
Definition charset.cpp:207
constexpr auto load_be(ParamTs &&... params)
Definition loadstor.h:504
std::vector< uint8_t > utf8_to_ucs2(std::string_view utf8)
Definition charset.cpp:137