1 // Copyright 2015-2016 Mozilla Foundation. See the COPYRIGHT
2 // file at the top-level directory of this distribution.
3 //
4 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
5 // https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
6 // <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
7 // option. This file may not be copied, modified, or distributed
8 // except according to those terms.
9
10 #pragma once
11
12 #ifndef encoding_rs_mem_cpp_h_
13 #define encoding_rs_mem_cpp_h_
14
15 #include <optional>
16 #include <string_view>
17 #include <tuple>
18 #include "gsl/gsl"
19
20 #include "encoding_rs_mem.h"
21
22 namespace encoding_rs {
23 namespace mem {
24
25 namespace detail {
26 /**
27 * Replaces `nullptr` with a bogus pointer suitable for use as part of a
28 * zero-length Rust slice.
29 */
30 template <class T>
null_to_bogus(T * ptr)31 static inline T* null_to_bogus(T* ptr) {
32 return ptr ? ptr : reinterpret_cast<T*>(alignof(T));
33 }
34 }; // namespace detail
35
36 /**
37 * Checks whether a potentially invalid UTF-16 buffer contains code points
38 * that trigger right-to-left processing or is all-Latin1.
39 *
40 * Possibly more efficient than performing the checks separately.
41 *
42 * Returns `Latin1Bidi::Latin1` if `is_utf16_latin1()` would return `true`.
43 * Otherwise, returns `Latin1Bidi::Bidi` if `is_utf16_bidi()` would return
44 * `true`. Otherwise, returns `Latin1Bidi::LeftToRight`.
45 */
check_for_latin1_and_bidi(std::u16string_view buffer)46 inline Latin1Bidi check_for_latin1_and_bidi(std::u16string_view buffer) {
47 return encoding_mem_check_utf16_for_latin1_and_bidi(
48 encoding_rs::mem::detail::null_to_bogus<const char16_t>(buffer.data()),
49 buffer.size());
50 }
51
52 /**
53 * Checks whether a potentially invalid UTF-8 buffer contains code points
54 * that trigger right-to-left processing or is all-Latin1.
55 *
56 * Possibly more efficient than performing the checks separately.
57 *
58 * Returns `Latin1Bidi::Latin1` if `is_utf8_latin1()` would return `true`.
59 *
60 * Otherwise, returns `Latin1Bidi::Bidi` if `is_utf8_bidi()` would return
61 * `true`. Otherwise, returns `Latin1Bidi::LeftToRight`.
62 */
check_for_latin1_and_bidi(std::string_view buffer)63 inline Latin1Bidi check_for_latin1_and_bidi(std::string_view buffer) {
64 return encoding_mem_check_utf8_for_latin1_and_bidi(
65 encoding_rs::mem::detail::null_to_bogus<const char>(buffer.data()),
66 buffer.size());
67 }
68
69 /**
70 * Converts bytes whose unsigned value is interpreted as Unicode code point
71 * (i.e. U+0000 to U+00FF, inclusive) to UTF-16.
72 *
73 * The length of the destination buffer must be at least the length of the
74 * source buffer.
75 *
76 * The number of `char16_t`s written equals the length of the source buffer.
77 *
78 * # Panics
79 *
80 * Panics if the destination buffer is shorter than stated above.
81 */
convert_latin1_to_utf16(gsl::span<const char> src,gsl::span<char16_t> dst)82 inline void convert_latin1_to_utf16(gsl::span<const char> src,
83 gsl::span<char16_t> dst) {
84 encoding_mem_convert_latin1_to_utf16(
85 encoding_rs::mem::detail::null_to_bogus<const char>(src.data()),
86 src.size(), encoding_rs::mem::detail::null_to_bogus<char16_t>(dst.data()),
87 dst.size());
88 }
89
90 /**
91 * Converts bytes whose unsigned value is interpreted as Unicode code point
92 * (i.e. U+0000 to U+00FF, inclusive) to UTF-8.
93 *
94 * The length of the destination buffer must be at least the length of the
95 * source buffer times two.
96 *
97 * Returns the number of bytes written.
98 *
99 * # Panics
100 *
101 * Panics if the destination buffer is shorter than stated above.
102 *
103 * # Safety
104 *
105 * Note that this function may write garbage beyond the number of bytes
106 * indicated by the return value.
107 *
108 * # Undefined behavior
109 *
110 * UB ensues if `src` and `dst` overlap.
111 */
convert_latin1_to_utf8(gsl::span<const char> src,gsl::span<char> dst)112 inline size_t convert_latin1_to_utf8(gsl::span<const char> src,
113 gsl::span<char> dst) {
114 return encoding_mem_convert_latin1_to_utf8(
115 encoding_rs::mem::detail::null_to_bogus<const char>(src.data()),
116 src.size(), encoding_rs::mem::detail::null_to_bogus<char>(dst.data()),
117 dst.size());
118 }
119
120 /**
121 * Converts bytes whose unsigned value is interpreted as Unicode code point
122 * (i.e. U+0000 to U+00FF, inclusive) to UTF-8 with potentially insufficient
123 * output space.
124 *
125 * Returns the number of bytes read and the number of bytes written.
126 *
127 * If the output isn't large enough, not all input is consumed.
128 *
129 * # Undefined behavior
130 *
131 * UB ensues if `src` and `dst` overlap.
132 */
convert_latin1_to_utf8_partial(gsl::span<const char> src,gsl::span<char> dst)133 inline std::tuple<size_t, size_t> convert_latin1_to_utf8_partial(
134 gsl::span<const char> src, gsl::span<char> dst) {
135 size_t src_read = src.size();
136 size_t dst_written = dst.size();
137 encoding_mem_convert_latin1_to_utf8_partial(
138 encoding_rs::mem::detail::null_to_bogus<const char>(src.data()),
139 &src_read, encoding_rs::mem::detail::null_to_bogus<char>(dst.data()),
140 &dst_written);
141 return {src_read, dst_written};
142 }
143
144 /**
145 * Converts valid UTF-8 to valid UTF-16.
146 *
147 * The length of the destination buffer must be at least the length of the
148 * source buffer.
149 *
150 * Returns the number of `char16_t`s written.
151 *
152 * # Panics
153 *
154 * Panics if the destination buffer is shorter than stated above.
155 */
convert_str_to_utf16(std::string_view src,gsl::span<char16_t> dst)156 inline size_t convert_str_to_utf16(std::string_view src,
157 gsl::span<char16_t> dst) {
158 return encoding_mem_convert_str_to_utf16(
159 encoding_rs::mem::detail::null_to_bogus<const char>(
160 reinterpret_cast<const char*>(src.data())),
161 src.size(), encoding_rs::mem::detail::null_to_bogus<char16_t>(dst.data()),
162 dst.size());
163 }
164
165 /**
166 * If the input is valid UTF-16 representing only Unicode code points from
167 * U+0000 to U+00FF, inclusive, converts the input into output that
168 * represents the value of each code point as the unsigned byte value of
169 * each output byte.
170 *
171 * If the input does not fulfill the condition stated above, does something
172 * that is memory-safe without any promises about any properties of the
173 * output and will probably assert in debug builds in future versions.
174 * In particular, callers shouldn't assume the output to be the same across
175 * crate versions or CPU architectures and should not assume that non-ASCII
176 * input can't map to ASCII output.
177 *
178 * The length of the destination buffer must be at least the length of the
179 * source buffer.
180 *
181 * The number of bytes written equals the length of the source buffer.
182 *
183 * # Panics
184 *
185 * Panics if the destination buffer is shorter than stated above.
186 * (Probably in future versions if debug assertions are enabled (and not
187 * fuzzing) and the input is not in the range U+0000 to U+00FF, inclusive.)
188 */
convert_utf16_to_latin1_lossy(std::u16string_view src,gsl::span<char> dst)189 inline void convert_utf16_to_latin1_lossy(std::u16string_view src,
190 gsl::span<char> dst) {
191 encoding_mem_convert_utf16_to_latin1_lossy(
192 encoding_rs::mem::detail::null_to_bogus<const char16_t>(src.data()),
193 src.size(), encoding_rs::mem::detail::null_to_bogus<char>(dst.data()),
194 dst.size());
195 }
196
197 /**
198 * Converts potentially-invalid UTF-16 to valid UTF-8 with errors replaced
199 * with the REPLACEMENT CHARACTER.
200 *
201 * The length of the destination buffer must be at least the length of the
202 * source buffer times three.
203 *
204 * Returns the number of bytes written.
205 *
206 * # Panics
207 *
208 * Panics if the destination buffer is shorter than stated above.
209 */
convert_utf16_to_utf8(std::u16string_view src,gsl::span<char> dst)210 inline size_t convert_utf16_to_utf8(std::u16string_view src,
211 gsl::span<char> dst) {
212 return encoding_mem_convert_utf16_to_utf8(
213 encoding_rs::mem::detail::null_to_bogus<const char16_t>(src.data()),
214 src.size(), encoding_rs::mem::detail::null_to_bogus<char>(dst.data()),
215 dst.size());
216 }
217
218 /**
219 * Converts potentially-invalid UTF-16 to valid UTF-8 with errors replaced
220 * with the REPLACEMENT CHARACTER with potentially insufficient output
221 * space.
222 *
223 * Returns the number of code units read and the number of bytes written.
224 *
225 * Guarantees that the bytes in the destination beyond the number of
226 * bytes claimed as written by the second item of the return tuple
227 * are left unmodified.
228 *
229 * Not all code units are read if there isn't enough output space.
230 * Note that this method isn't designed for general streamability but for
231 * not allocating memory for the worst case up front. Specifically,
232 * if the input starts with or ends with an unpaired surrogate, those are
233 * replaced with the REPLACEMENT CHARACTER.
234 *
235 * Matches the semantics of `TextEncoder.encodeInto()` from the
236 * Encoding Standard.
237 */
convert_utf16_to_utf8_partial(std::u16string_view src,gsl::span<char> dst)238 inline std::tuple<size_t, size_t> convert_utf16_to_utf8_partial(
239 std::u16string_view src, gsl::span<char> dst) {
240 size_t src_read = src.size();
241 size_t dst_written = dst.size();
242 encoding_mem_convert_utf16_to_utf8_partial(
243 encoding_rs::mem::detail::null_to_bogus<const char16_t>(src.data()),
244 &src_read, encoding_rs::mem::detail::null_to_bogus<char>(dst.data()),
245 &dst_written);
246 return {src_read, dst_written};
247 }
248
249 /**
250 * If the input is valid UTF-8 representing only Unicode code points from
251 * U+0000 to U+00FF, inclusive, converts the input into output that
252 * represents the value of each code point as the unsigned byte value of
253 * each output byte.
254 *
255 * If the input does not fulfill the condition stated above, this function
256 * panics if debug assertions are enabled (and fuzzing isn't) and otherwise
257 * does something that is memory-safe without any promises about any
258 * properties of the output. In particular, callers shouldn't assume the
259 * output to be the same across crate versions or CPU architectures and
260 * should not assume that non-ASCII input can't map to ASCII output.
261 * The length of the destination buffer must be at least the length of the
262 * source buffer.
263 *
264 * Returns the number of bytes written.
265 *
266 * # Panics
267 *
268 * Panics if the destination buffer is shorter than stated above.
269 * If debug assertions are enabled (and not fuzzing) and the input is
270 * not in the range U+0000 to U+00FF, inclusive.
271 *
272 * # Undefined behavior
273 *
274 * UB ensues if `src` and `dst` overlap.
275 */
convert_utf8_to_latin1_lossy(std::string_view src,gsl::span<char> dst)276 inline size_t convert_utf8_to_latin1_lossy(std::string_view src,
277 gsl::span<char> dst) {
278 return encoding_mem_convert_utf8_to_latin1_lossy(
279 encoding_rs::mem::detail::null_to_bogus<const char>(
280 reinterpret_cast<const char*>(src.data())),
281 src.size(), encoding_rs::mem::detail::null_to_bogus<char>(dst.data()),
282 dst.size());
283 }
284
285 /**
286 * Converts potentially-invalid UTF-8 to valid UTF-16 with errors replaced
287 * with the REPLACEMENT CHARACTER.
288 *
289 * The length of the destination buffer must be at least the length of the
290 * source buffer _plus one_.
291 *
292 * Returns the number of `char16_t`s written.
293 *
294 * # Panics
295 *
296 * Panics if the destination buffer is shorter than stated above.
297 */
convert_utf8_to_utf16(std::string_view src,gsl::span<char16_t> dst)298 inline size_t convert_utf8_to_utf16(std::string_view src,
299 gsl::span<char16_t> dst) {
300 return encoding_mem_convert_utf8_to_utf16(
301 encoding_rs::mem::detail::null_to_bogus<const char>(
302 reinterpret_cast<const char*>(src.data())),
303 src.size(), encoding_rs::mem::detail::null_to_bogus<char16_t>(dst.data()),
304 dst.size());
305 }
306
307 /**
308 * Converts potentially-invalid UTF-8 to valid UTF-16 signaling on error.
309 *
310 * The length of the destination buffer must be at least the length of the
311 * source buffer.
312 *
313 * Returns the number of `char16_t`s written or `std::nullopt` if the input was
314 * invalid.
315 *
316 * When the input was invalid, some output may have been written.
317 *
318 * # Panics
319 *
320 * Panics if the destination buffer is shorter than stated above.
321 */
convert_utf8_to_utf16_without_replacement(std::string_view src,gsl::span<char16_t> dst)322 inline std::optional<size_t> convert_utf8_to_utf16_without_replacement(
323 std::string_view src, gsl::span<char16_t> dst) {
324 size_t val = encoding_mem_convert_utf8_to_utf16_without_replacement(
325 encoding_rs::mem::detail::null_to_bogus<const char>(
326 reinterpret_cast<const char*>(src.data())),
327 src.size(), encoding_rs::mem::detail::null_to_bogus<char16_t>(dst.data()),
328 dst.size());
329 if (val == SIZE_MAX) {
330 return std::nullopt;
331 }
332 return val;
333 }
334
335 /**
336 * Copies ASCII from source to destination up to the first non-ASCII byte
337 * (or the end of the input if it is ASCII in its entirety).
338 *
339 * The length of the destination buffer must be at least the length of the
340 * source buffer.
341 *
342 * Returns the number of bytes written.
343 *
344 * # Panics
345 *
346 * Panics if the destination buffer is shorter than stated above.
347 *
348 * # Undefined behavior
349 *
350 * UB ensues if `src` and `dst` overlap.
351 */
copy_ascii_to_ascii(gsl::span<const char> src,gsl::span<char> dst)352 inline size_t copy_ascii_to_ascii(gsl::span<const char> src,
353 gsl::span<char> dst) {
354 return encoding_mem_copy_ascii_to_ascii(
355 encoding_rs::mem::detail::null_to_bogus<const char>(src.data()),
356 src.size(), encoding_rs::mem::detail::null_to_bogus<char>(dst.data()),
357 dst.size());
358 }
359
360 /**
361 * Copies ASCII from source to destination zero-extending it to UTF-16 up to
362 * the first non-ASCII byte (or the end of the input if it is ASCII in its
363 * entirety).
364 *
365 * The length of the destination buffer must be at least the length of the
366 * source buffer.
367 *
368 * Returns the number of `char16_t`s written.
369 *
370 * # Panics
371 *
372 * Panics if the destination buffer is shorter than stated above.
373 */
copy_ascii_to_basic_latin(gsl::span<const char> src,gsl::span<char16_t> dst)374 inline size_t copy_ascii_to_basic_latin(gsl::span<const char> src,
375 gsl::span<char16_t> dst) {
376 return encoding_mem_copy_ascii_to_basic_latin(
377 encoding_rs::mem::detail::null_to_bogus<const char>(src.data()),
378 src.size(), encoding_rs::mem::detail::null_to_bogus<char16_t>(dst.data()),
379 dst.size());
380 }
381
382 /**
383 * Copies Basic Latin from source to destination narrowing it to ASCII up to
384 * the first non-Basic Latin code unit (or the end of the input if it is
385 * Basic Latin in its entirety).
386 *
387 * The length of the destination buffer must be at least the length of the
388 * source buffer.
389 *
390 * Returns the number of bytes written.
391 *
392 * # Panics
393 *
394 * Panics if the destination buffer is shorter than stated above.
395 */
copy_basic_latin_to_ascii(gsl::span<const char16_t> src,gsl::span<char> dst)396 inline size_t copy_basic_latin_to_ascii(gsl::span<const char16_t> src,
397 gsl::span<char> dst) {
398 return encoding_mem_copy_basic_latin_to_ascii(
399 encoding_rs::mem::detail::null_to_bogus<const char16_t>(src.data()),
400 src.size(), encoding_rs::mem::detail::null_to_bogus<char>(dst.data()),
401 dst.size());
402 }
403
404 /**
405 * Replaces unpaired surrogates in the input with the REPLACEMENT CHARACTER.
406 */
ensure_utf16_validity(gsl::span<char16_t> buffer)407 inline void ensure_utf16_validity(gsl::span<char16_t> buffer) {
408 encoding_mem_ensure_utf16_validity(
409 encoding_rs::mem::detail::null_to_bogus<char16_t>(buffer.data()),
410 buffer.size());
411 }
412
413 /**
414 * Checks whether the buffer is all-ASCII.
415 *
416 * May read the entire buffer even if it isn't all-ASCII. (I.e. the function
417 * is not guaranteed to fail fast.)
418 */
is_ascii(std::string_view buffer)419 inline bool is_ascii(std::string_view buffer) {
420 return encoding_mem_is_ascii(
421 encoding_rs::mem::detail::null_to_bogus<const char>(buffer.data()),
422 buffer.size());
423 }
424
425 /**
426 * Checks whether the buffer is all-Basic Latin (i.e. UTF-16 representing
427 * only ASCII characters).
428 *
429 * May read the entire buffer even if it isn't all-ASCII. (I.e. the function
430 * is not guaranteed to fail fast.)
431 */
is_ascii(std::u16string_view buffer)432 inline bool is_ascii(std::u16string_view buffer) {
433 return encoding_mem_is_basic_latin(
434 encoding_rs::mem::detail::null_to_bogus<const char16_t>(buffer.data()),
435 buffer.size());
436 }
437
438 /**
439 * Checks whether a scalar value triggers right-to-left processing.
440 *
441 * The check is done on a Unicode block basis without regard to assigned
442 * vs. unassigned code points in the block. Hebrew presentation forms in
443 * the Alphabetic Presentation Forms block are treated as if they formed
444 * a block on their own (i.e. it treated as right-to-left). Additionally,
445 * the four RIGHT-TO-LEFT FOO controls in General Punctuation are checked
446 * for. Control characters that are technically bidi controls but do not
447 * cause right-to-left behavior without the presence of right-to-left
448 * characters or right-to-left controls are not checked for. As a special
449 * case, U+FEFF is excluded from Arabic Presentation Forms-B.
450 *
451 * # Undefined behavior
452 *
453 * Undefined behavior ensues if `c` is not a valid Unicode Scalar Value.
454 */
is_scalar_value_bidi(char32_t c)455 inline bool is_scalar_value_bidi(char32_t c) {
456 return encoding_mem_is_char_bidi(c);
457 }
458
459 /**
460 * Checks whether a UTF-16 buffer contains code points that trigger
461 * right-to-left processing.
462 *
463 * The check is done on a Unicode block basis without regard to assigned
464 * vs. unassigned code points in the block. Hebrew presentation forms in
465 * the Alphabetic Presentation Forms block are treated as if they formed
466 * a block on their own (i.e. it treated as right-to-left). Additionally,
467 * the four RIGHT-TO-LEFT FOO controls in General Punctuation are checked
468 * for. Control characters that are technically bidi controls but do not
469 * cause right-to-left behavior without the presence of right-to-left
470 * characters or right-to-left controls are not checked for. As a special
471 * case, U+FEFF is excluded from Arabic Presentation Forms-B.
472 * Returns `true` if the input contains an RTL character or an unpaired
473 * high surrogate that could be the high half of an RTL character.
474 * Returns `false` if the input contains neither RTL characters nor
475 * unpaired high surrogates that could be higher halves of RTL characters.
476 */
is_bidi(std::u16string_view buffer)477 inline bool is_bidi(std::u16string_view buffer) {
478 return encoding_mem_is_utf16_bidi(
479 encoding_rs::mem::detail::null_to_bogus<const char16_t>(buffer.data()),
480 buffer.size());
481 }
482
483 /**
484 * Checks whether a UTF-16 code unit triggers right-to-left processing.
485 *
486 * The check is done on a Unicode block basis without regard to assigned
487 * vs. unassigned code points in the block. Hebrew presentation forms in
488 * the Alphabetic Presentation Forms block are treated as if they formed
489 * a block on their own (i.e. it treated as right-to-left). Additionally,
490 * the four RIGHT-TO-LEFT FOO controls in General Punctuation are checked
491 * for. Control characters that are technically bidi controls but do not
492 * cause right-to-left behavior without the presence of right-to-left
493 * characters or right-to-left controls are not checked for. As a special
494 * case, U+FEFF is excluded from Arabic Presentation Forms-B.
495 * Since supplementary-plane right-to-left blocks are identifiable from the
496 * high surrogate without examining the low surrogate, this function returns
497 * `true` for such high surrogates making the function suitable for handling
498 * supplementary-plane text without decoding surrogate pairs to scalar
499 * values. Obviously, such high surrogates are then reported as right-to-left
500 * even if actually unpaired.
501 */
is_utf16_code_unit_bidi(char16_t u)502 inline bool is_utf16_code_unit_bidi(char16_t u) {
503 return encoding_mem_is_utf16_code_unit_bidi(u);
504 }
505
506 /**
507 * Checks whether the buffer represents only code point less than or equal
508 * to U+00FF.
509 *
510 * May read the entire buffer even if it isn't all-Latin1. (I.e. the function
511 * is not guaranteed to fail fast.)
512 */
is_utf16_latin1(std::u16string_view buffer)513 inline bool is_utf16_latin1(std::u16string_view buffer) {
514 return encoding_mem_is_utf16_latin1(
515 encoding_rs::mem::detail::null_to_bogus<const char16_t>(buffer.data()),
516 buffer.size());
517 }
518
519 /**
520 * Checks whether a potentially-invalid UTF-8 buffer contains code points
521 * that trigger right-to-left processing.
522 *
523 * The check is done on a Unicode block basis without regard to assigned
524 * vs. unassigned code points in the block. Hebrew presentation forms in
525 * the Alphabetic Presentation Forms block are treated as if they formed
526 * a block on their own (i.e. it treated as right-to-left). Additionally,
527 * the four RIGHT-TO-LEFT FOO controls in General Punctuation are checked
528 * for. Control characters that are technically bidi controls but do not
529 * cause right-to-left behavior without the presence of right-to-left
530 * characters or right-to-left controls are not checked for. As a special
531 * case, U+FEFF is excluded from Arabic Presentation Forms-B.
532 * Returns `true` if the input is invalid UTF-8 or the input contains an
533 * RTL character. Returns `false` if the input is valid UTF-8 and contains
534 * no RTL characters.
535 */
is_bidi(std::string_view buffer)536 inline bool is_bidi(std::string_view buffer) {
537 return encoding_mem_is_utf8_bidi(
538 encoding_rs::mem::detail::null_to_bogus<const char>(buffer.data()),
539 buffer.size());
540 }
541
542 /**
543 * Checks whether the buffer is valid UTF-8 representing only code points
544 * less than or equal to U+00FF.
545 *
546 * Fails fast. (I.e. returns before having read the whole buffer if UTF-8
547 * invalidity or code points above U+00FF are discovered.
548 */
is_utf8_latin1(std::string_view buffer)549 inline bool is_utf8_latin1(std::string_view buffer) {
550 return encoding_mem_is_utf8_latin1(
551 encoding_rs::mem::detail::null_to_bogus<const char>(buffer.data()),
552 buffer.size());
553 }
554
555 /**
556 * Returns the index of the first unpaired surrogate or, if the input is
557 * valid UTF-16 in its entirety, the length of the input.
558 */
utf16_valid_up_to(std::u16string_view buffer)559 inline size_t utf16_valid_up_to(std::u16string_view buffer) {
560 return encoding_mem_utf16_valid_up_to(
561 encoding_rs::mem::detail::null_to_bogus<const char16_t>(buffer.data()),
562 buffer.size());
563 }
564
565 /**
566 * Returns the index of first byte that starts a non-Latin1 byte
567 * sequence, or the length of the string if there are none.
568 */
utf8_latin1_up_to(std::string_view buffer)569 inline size_t utf8_latin1_up_to(std::string_view buffer) {
570 return encoding_mem_utf8_latin1_up_to(
571 encoding_rs::mem::detail::null_to_bogus<const char>(buffer.data()),
572 buffer.size());
573 }
574
575 }; // namespace mem
576 }; // namespace encoding_rs
577
578 #endif // encoding_rs_mem_cpp_h_
579