Branch data Line data Source code
1 : : /*-------------------------------------------------------------------------
2 : : *
3 : : * Utility functions for conversion procs.
4 : : *
5 : : * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
6 : : * Portions Copyright (c) 1994, Regents of the University of California
7 : : *
8 : : * IDENTIFICATION
9 : : * src/backend/utils/mb/conv.c
10 : : *
11 : : *-------------------------------------------------------------------------
12 : : */
13 : : #include "postgres.h"
14 : : #include "mb/pg_wchar.h"
15 : :
16 : :
17 : : /*
18 : : * local2local: a generic single byte charset encoding
19 : : * conversion between two ASCII-superset encodings.
20 : : *
21 : : * l points to the source string of length len
22 : : * p is the output area (must be large enough!)
23 : : * src_encoding is the PG identifier for the source encoding
24 : : * dest_encoding is the PG identifier for the target encoding
25 : : * tab holds conversion entries for the source charset
26 : : * starting from 128 (0x80). each entry in the table holds the corresponding
27 : : * code point for the target charset, or 0 if there is no equivalent code.
28 : : *
29 : : * Returns the number of input bytes consumed. If noError is true, this can
30 : : * be less than 'len'.
31 : : */
32 : : int
33 : 38 : local2local(const unsigned char *l,
34 : : unsigned char *p,
35 : : int len,
36 : : int src_encoding,
37 : : int dest_encoding,
38 : : const unsigned char *tab,
39 : : bool noError)
40 : : {
41 : 38 : const unsigned char *start = l;
42 : 38 : unsigned char c1,
43 : : c2;
44 : :
45 [ + + ]: 122 : while (len > 0)
46 : : {
47 : 102 : c1 = *l;
48 [ + + ]: 102 : if (c1 == 0)
49 : : {
50 [ + + ]: 18 : if (noError)
51 : 9 : break;
52 : 9 : report_invalid_encoding(src_encoding, (const char *) l, len);
53 : : }
54 [ + + ]: 84 : if (!IS_HIGHBIT_SET(c1))
55 : 51 : *p++ = c1;
56 : : else
57 : : {
58 : 33 : c2 = tab[c1 - HIGHBIT];
59 [ + - ]: 33 : if (c2)
60 : 33 : *p++ = c2;
61 : : else
62 : : {
63 [ # # ]: 0 : if (noError)
64 : 0 : break;
65 : 0 : report_untranslatable_char(src_encoding, dest_encoding,
66 : 0 : (const char *) l, len);
67 : : }
68 : : }
69 : 84 : l++;
70 : 84 : len--;
71 : : }
72 : 29 : *p = '\0';
73 : :
74 : 58 : return l - start;
75 : 29 : }
76 : :
77 : : /*
78 : : * LATINn ---> MIC when the charset's local codes map directly to MIC
79 : : *
80 : : * l points to the source string of length len
81 : : * p is the output area (must be large enough!)
82 : : * lc is the mule character set id for the local encoding
83 : : * encoding is the PG identifier for the local encoding
84 : : *
85 : : * Returns the number of input bytes consumed. If noError is true, this can
86 : : * be less than 'len'.
87 : : */
88 : : int
89 : 5 : latin2mic(const unsigned char *l, unsigned char *p, int len,
90 : : int lc, int encoding, bool noError)
91 : : {
92 : 5 : const unsigned char *start = l;
93 : 5 : int c1;
94 : :
95 [ + + ]: 20 : while (len > 0)
96 : : {
97 : 15 : c1 = *l;
98 [ + - ]: 15 : if (c1 == 0)
99 : : {
100 [ # # ]: 0 : if (noError)
101 : 0 : break;
102 : 0 : report_invalid_encoding(encoding, (const char *) l, len);
103 : : }
104 [ + - ]: 15 : if (IS_HIGHBIT_SET(c1))
105 : 0 : *p++ = lc;
106 : 15 : *p++ = c1;
107 : 15 : l++;
108 : 15 : len--;
109 : : }
110 : 5 : *p = '\0';
111 : :
112 : 10 : return l - start;
113 : 5 : }
114 : :
115 : : /*
116 : : * MIC ---> LATINn when the charset's local codes map directly to MIC
117 : : *
118 : : * mic points to the source string of length len
119 : : * p is the output area (must be large enough!)
120 : : * lc is the mule character set id for the local encoding
121 : : * encoding is the PG identifier for the local encoding
122 : : *
123 : : * Returns the number of input bytes consumed. If noError is true, this can
124 : : * be less than 'len'.
125 : : */
126 : : int
127 : 59 : mic2latin(const unsigned char *mic, unsigned char *p, int len,
128 : : int lc, int encoding, bool noError)
129 : : {
130 : 59 : const unsigned char *start = mic;
131 : 59 : int c1;
132 : :
133 [ + + ]: 140 : while (len > 0)
134 : : {
135 : 129 : c1 = *mic;
136 [ + - ]: 129 : if (c1 == 0)
137 : : {
138 [ # # ]: 0 : if (noError)
139 : 0 : break;
140 : 0 : report_invalid_encoding(PG_MULE_INTERNAL, (const char *) mic, len);
141 : : }
142 [ + + ]: 129 : if (!IS_HIGHBIT_SET(c1))
143 : : {
144 : : /* easy for ASCII */
145 : 60 : *p++ = c1;
146 : 60 : mic++;
147 : 60 : len--;
148 : 60 : }
149 : : else
150 : : {
151 : 69 : int l = pg_mule_mblen(mic);
152 : :
153 [ + + ]: 69 : if (len < l)
154 : : {
155 [ + + ]: 18 : if (noError)
156 : 9 : break;
157 : 18 : report_invalid_encoding(PG_MULE_INTERNAL, (const char *) mic,
158 : 9 : len);
159 : : }
160 [ + + + - : 51 : if (l != 2 || c1 != lc || !IS_HIGHBIT_SET(mic[1]))
+ + ]
161 : : {
162 [ + + ]: 30 : if (noError)
163 : 15 : break;
164 : 30 : report_untranslatable_char(PG_MULE_INTERNAL, encoding,
165 : 15 : (const char *) mic, len);
166 : : }
167 : 21 : *p++ = mic[1];
168 : 21 : mic += 2;
169 : 21 : len -= 2;
170 [ - + + ]: 45 : }
171 : : }
172 : 35 : *p = '\0';
173 : :
174 : 70 : return mic - start;
175 : 35 : }
176 : :
177 : :
178 : : /*
179 : : * latin2mic_with_table: a generic single byte charset encoding
180 : : * conversion from a local charset to the mule internal code.
181 : : *
182 : : * l points to the source string of length len
183 : : * p is the output area (must be large enough!)
184 : : * lc is the mule character set id for the local encoding
185 : : * encoding is the PG identifier for the local encoding
186 : : * tab holds conversion entries for the local charset
187 : : * starting from 128 (0x80). each entry in the table holds the corresponding
188 : : * code point for the mule encoding, or 0 if there is no equivalent code.
189 : : *
190 : : * Returns the number of input bytes consumed. If noError is true, this can
191 : : * be less than 'len'.
192 : : */
193 : : int
194 : 28 : latin2mic_with_table(const unsigned char *l,
195 : : unsigned char *p,
196 : : int len,
197 : : int lc,
198 : : int encoding,
199 : : const unsigned char *tab,
200 : : bool noError)
201 : : {
202 : 28 : const unsigned char *start = l;
203 : 28 : unsigned char c1,
204 : : c2;
205 : :
206 [ + + ]: 82 : while (len > 0)
207 : : {
208 : 72 : c1 = *l;
209 [ + + ]: 72 : if (c1 == 0)
210 : : {
211 [ + + ]: 18 : if (noError)
212 : 9 : break;
213 : 9 : report_invalid_encoding(encoding, (const char *) l, len);
214 : : }
215 [ + + ]: 54 : if (!IS_HIGHBIT_SET(c1))
216 : 21 : *p++ = c1;
217 : : else
218 : : {
219 : 33 : c2 = tab[c1 - HIGHBIT];
220 [ + - ]: 33 : if (c2)
221 : : {
222 : 33 : *p++ = lc;
223 : 33 : *p++ = c2;
224 : 33 : }
225 : : else
226 : : {
227 [ # # ]: 0 : if (noError)
228 : 0 : break;
229 : 0 : report_untranslatable_char(encoding, PG_MULE_INTERNAL,
230 : 0 : (const char *) l, len);
231 : : }
232 : : }
233 : 54 : l++;
234 : 54 : len--;
235 : : }
236 : 19 : *p = '\0';
237 : :
238 : 38 : return l - start;
239 : 19 : }
240 : :
241 : : /*
242 : : * mic2latin_with_table: a generic single byte charset encoding
243 : : * conversion from the mule internal code to a local charset.
244 : : *
245 : : * mic points to the source string of length len
246 : : * p is the output area (must be large enough!)
247 : : * lc is the mule character set id for the local encoding
248 : : * encoding is the PG identifier for the local encoding
249 : : * tab holds conversion entries for the mule internal code's second byte,
250 : : * starting from 128 (0x80). each entry in the table holds the corresponding
251 : : * code point for the local charset, or 0 if there is no equivalent code.
252 : : *
253 : : * Returns the number of input bytes consumed. If noError is true, this can
254 : : * be less than 'len'.
255 : : */
256 : : int
257 : 58 : mic2latin_with_table(const unsigned char *mic,
258 : : unsigned char *p,
259 : : int len,
260 : : int lc,
261 : : int encoding,
262 : : const unsigned char *tab,
263 : : bool noError)
264 : : {
265 : 58 : const unsigned char *start = mic;
266 : 58 : unsigned char c1,
267 : : c2;
268 : :
269 [ + + ]: 136 : while (len > 0)
270 : : {
271 : 126 : c1 = *mic;
272 [ + - ]: 126 : if (c1 == 0)
273 : : {
274 [ # # ]: 0 : if (noError)
275 : 0 : break;
276 : 0 : report_invalid_encoding(PG_MULE_INTERNAL, (const char *) mic, len);
277 : : }
278 [ + + ]: 126 : if (!IS_HIGHBIT_SET(c1))
279 : : {
280 : : /* easy for ASCII */
281 : 57 : *p++ = c1;
282 : 57 : mic++;
283 : 57 : len--;
284 : 57 : }
285 : : else
286 : : {
287 : 69 : int l = pg_mule_mblen(mic);
288 : :
289 [ + + ]: 69 : if (len < l)
290 : : {
291 [ + + ]: 18 : if (noError)
292 : 9 : break;
293 : 18 : report_invalid_encoding(PG_MULE_INTERNAL, (const char *) mic,
294 : 9 : len);
295 : : }
296 [ + + + - : 51 : if (l != 2 || c1 != lc || !IS_HIGHBIT_SET(mic[1]) ||
+ + - + ]
297 : 21 : (c2 = tab[mic[1] - HIGHBIT]) == 0)
298 : : {
299 [ + + ]: 30 : if (noError)
300 : 15 : break;
301 : 30 : report_untranslatable_char(PG_MULE_INTERNAL, encoding,
302 : 15 : (const char *) mic, len);
303 : : break; /* keep compiler quiet */
304 : : }
305 : 21 : *p++ = c2;
306 : 21 : mic += 2;
307 : 21 : len -= 2;
308 [ - + + ]: 45 : }
309 : : }
310 : 34 : *p = '\0';
311 : :
312 : 68 : return mic - start;
313 : 34 : }
314 : :
315 : : /*
316 : : * comparison routine for bsearch()
317 : : * this routine is intended for combined UTF8 -> local code
318 : : */
319 : : static int
320 : 78 : compare3(const void *p1, const void *p2)
321 : : {
322 : 78 : uint32 s1,
323 : : s2,
324 : : d1,
325 : : d2;
326 : :
327 : 78 : s1 = *(const uint32 *) p1;
328 : 78 : s2 = *((const uint32 *) p1 + 1);
329 : 78 : d1 = ((const pg_utf_to_local_combined *) p2)->utf1;
330 : 78 : d2 = ((const pg_utf_to_local_combined *) p2)->utf2;
331 [ + + + + : 78 : return (s1 > d1 || (s1 == d1 && s2 > d2)) ? 1 : ((s1 == d1 && s2 == d2) ? 0 : -1);
- + + + ]
332 : 78 : }
333 : :
334 : : /*
335 : : * comparison routine for bsearch()
336 : : * this routine is intended for local code -> combined UTF8
337 : : */
338 : : static int
339 : 27 : compare4(const void *p1, const void *p2)
340 : : {
341 : 27 : uint32 v1,
342 : : v2;
343 : :
344 : 27 : v1 = *(const uint32 *) p1;
345 : 27 : v2 = ((const pg_local_to_utf_combined *) p2)->code;
346 [ + + ]: 27 : return (v1 > v2) ? 1 : ((v1 == v2) ? 0 : -1);
347 : 27 : }
348 : :
349 : : /*
350 : : * store 32bit character representation into multibyte stream
351 : : */
352 : : static inline unsigned char *
353 : 192 : store_coded_char(unsigned char *dest, uint32 code)
354 : : {
355 [ + + ]: 192 : if (code & 0xff000000)
356 : 21 : *dest++ = code >> 24;
357 [ + + ]: 192 : if (code & 0x00ff0000)
358 : 90 : *dest++ = code >> 16;
359 [ + + ]: 192 : if (code & 0x0000ff00)
360 : 171 : *dest++ = code >> 8;
361 [ - + ]: 192 : if (code & 0x000000ff)
362 : 192 : *dest++ = code;
363 : 192 : return dest;
364 : : }
365 : :
366 : : /*
367 : : * Convert a character using a conversion radix tree.
368 : : *
369 : : * 'l' is the length of the input character in bytes, and b1-b4 are
370 : : * the input character's bytes.
371 : : */
372 : : static inline uint32
373 : 333 : pg_mb_radix_conv(const pg_mb_radix_tree *rt,
374 : : int l,
375 : : unsigned char b1,
376 : : unsigned char b2,
377 : : unsigned char b3,
378 : : unsigned char b4)
379 : : {
380 [ + + ]: 333 : if (l == 4)
381 : : {
382 : : /* 4-byte code */
383 : :
384 : : /* check code validity */
385 [ + - + - ]: 15 : if (b1 < rt->b4_1_lower || b1 > rt->b4_1_upper ||
386 [ + - + - ]: 15 : b2 < rt->b4_2_lower || b2 > rt->b4_2_upper ||
387 [ + - + - ]: 15 : b3 < rt->b4_3_lower || b3 > rt->b4_3_upper ||
388 [ + - - + ]: 15 : b4 < rt->b4_4_lower || b4 > rt->b4_4_upper)
389 : 0 : return 0;
390 : :
391 : : /* perform lookup */
392 [ + - ]: 15 : if (rt->chars32)
393 : : {
394 : 15 : uint32 idx = rt->b4root;
395 : :
396 : 15 : idx = rt->chars32[b1 + idx - rt->b4_1_lower];
397 : 15 : idx = rt->chars32[b2 + idx - rt->b4_2_lower];
398 : 15 : idx = rt->chars32[b3 + idx - rt->b4_3_lower];
399 : 15 : return rt->chars32[b4 + idx - rt->b4_4_lower];
400 : 15 : }
401 : : else
402 : : {
403 : 0 : uint16 idx = rt->b4root;
404 : :
405 : 0 : idx = rt->chars16[b1 + idx - rt->b4_1_lower];
406 : 0 : idx = rt->chars16[b2 + idx - rt->b4_2_lower];
407 : 0 : idx = rt->chars16[b3 + idx - rt->b4_3_lower];
408 : 0 : return rt->chars16[b4 + idx - rt->b4_4_lower];
409 : 0 : }
410 : : }
411 [ + + ]: 318 : else if (l == 3)
412 : : {
413 : : /* 3-byte code */
414 : :
415 : : /* check code validity */
416 [ + - + + ]: 156 : if (b2 < rt->b3_1_lower || b2 > rt->b3_1_upper ||
417 [ + - + - ]: 48 : b3 < rt->b3_2_lower || b3 > rt->b3_2_upper ||
418 [ + - - + ]: 48 : b4 < rt->b3_3_lower || b4 > rt->b3_3_upper)
419 : 108 : return 0;
420 : :
421 : : /* perform lookup */
422 [ + - ]: 48 : if (rt->chars32)
423 : : {
424 : 48 : uint32 idx = rt->b3root;
425 : :
426 : 48 : idx = rt->chars32[b2 + idx - rt->b3_1_lower];
427 : 48 : idx = rt->chars32[b3 + idx - rt->b3_2_lower];
428 : 48 : return rt->chars32[b4 + idx - rt->b3_3_lower];
429 : 48 : }
430 : : else
431 : : {
432 : 0 : uint16 idx = rt->b3root;
433 : :
434 : 0 : idx = rt->chars16[b2 + idx - rt->b3_1_lower];
435 : 0 : idx = rt->chars16[b3 + idx - rt->b3_2_lower];
436 : 0 : return rt->chars16[b4 + idx - rt->b3_3_lower];
437 : 0 : }
438 : : }
439 [ + + ]: 162 : else if (l == 2)
440 : : {
441 : : /* 2-byte code */
442 : :
443 : : /* check code validity - first byte */
444 [ + - + + ]: 129 : if (b3 < rt->b2_1_lower || b3 > rt->b2_1_upper ||
445 [ + - - + ]: 117 : b4 < rt->b2_2_lower || b4 > rt->b2_2_upper)
446 : 12 : return 0;
447 : :
448 : : /* perform lookup */
449 [ + + ]: 117 : if (rt->chars32)
450 : : {
451 : 90 : uint32 idx = rt->b2root;
452 : :
453 : 90 : idx = rt->chars32[b3 + idx - rt->b2_1_lower];
454 : 90 : return rt->chars32[b4 + idx - rt->b2_2_lower];
455 : 90 : }
456 : : else
457 : : {
458 : 27 : uint16 idx = rt->b2root;
459 : :
460 : 27 : idx = rt->chars16[b3 + idx - rt->b2_1_lower];
461 : 27 : return rt->chars16[b4 + idx - rt->b2_2_lower];
462 : 27 : }
463 : : }
464 [ + - ]: 33 : else if (l == 1)
465 : : {
466 : : /* 1-byte code */
467 : :
468 : : /* check code validity - first byte */
469 [ + - - + ]: 33 : if (b4 < rt->b1_lower || b4 > rt->b1_upper)
470 : 0 : return 0;
471 : :
472 : : /* perform lookup */
473 [ + - ]: 33 : if (rt->chars32)
474 : 33 : return rt->chars32[b4 + rt->b1root - rt->b1_lower];
475 : : else
476 : 0 : return rt->chars16[b4 + rt->b1root - rt->b1_lower];
477 : : }
478 : 0 : return 0; /* shouldn't happen */
479 : 333 : }
480 : :
481 : : /*
482 : : * UTF8 ---> local code
483 : : *
484 : : * utf: input string in UTF8 encoding (need not be null-terminated)
485 : : * len: length of input string (in bytes)
486 : : * iso: pointer to the output area (must be large enough!)
487 : : * (output string will be null-terminated)
488 : : * map: conversion map for single characters
489 : : * cmap: conversion map for combined characters
490 : : * (optional, pass NULL if none)
491 : : * cmapsize: number of entries in the conversion map for combined characters
492 : : * (optional, pass 0 if none)
493 : : * conv_func: algorithmic encoding conversion function
494 : : * (optional, pass NULL if none)
495 : : * encoding: PG identifier for the local encoding
496 : : *
497 : : * For each character, the cmap (if provided) is consulted first; if no match,
498 : : * the map is consulted next; if still no match, the conv_func (if provided)
499 : : * is applied. An error is raised if no match is found.
500 : : *
501 : : * See pg_wchar.h for more details about the data structures used here.
502 : : *
503 : : * Returns the number of input bytes consumed. If noError is true, this can
504 : : * be less than 'len'.
505 : : */
506 : : int
507 : 378 : UtfToLocal(const unsigned char *utf, int len,
508 : : unsigned char *iso,
509 : : const pg_mb_radix_tree *map,
510 : : const pg_utf_to_local_combined *cmap, int cmapsize,
511 : : utf_local_conversion_func conv_func,
512 : : int encoding, bool noError)
513 : : {
514 : 378 : uint32 iutf;
515 : 378 : int l;
516 : 378 : const pg_utf_to_local_combined *cp;
517 : 378 : const unsigned char *start = utf;
518 : :
519 [ + - ]: 378 : if (!PG_VALID_ENCODING(encoding))
520 [ # # # # ]: 0 : ereport(ERROR,
521 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
522 : : errmsg("invalid encoding number: %d", encoding)));
523 : :
524 [ + + ]: 1176 : for (; len > 0; len -= l)
525 : : {
526 : 1068 : unsigned char b1 = 0;
527 : 1068 : unsigned char b2 = 0;
528 : 1068 : unsigned char b3 = 0;
529 : 1068 : unsigned char b4 = 0;
530 : :
531 : : /* "break" cases all represent errors */
532 [ + + ]: 1068 : if (*utf == '\0')
533 : 30 : break;
534 : :
535 : 1038 : l = pg_utf_mblen(utf);
536 [ + + ]: 1038 : if (len < l)
537 : 36 : break;
538 : :
539 [ + + ]: 1002 : if (!pg_utf8_islegal(utf, l))
540 : 60 : break;
541 : :
542 [ + + ]: 942 : if (l == 1)
543 : : {
544 : : /* ASCII case is easy, assume it's one-to-one conversion */
545 : 708 : *iso++ = *utf++;
546 : 708 : continue;
547 : : }
548 : :
549 : : /* collect coded char of length l */
550 [ + + ]: 234 : if (l == 2)
551 : : {
552 : 69 : b3 = *utf++;
553 : 69 : b4 = *utf++;
554 : 69 : }
555 [ + - ]: 165 : else if (l == 3)
556 : : {
557 : 165 : b2 = *utf++;
558 : 165 : b3 = *utf++;
559 : 165 : b4 = *utf++;
560 : 165 : }
561 [ # # ]: 0 : else if (l == 4)
562 : : {
563 : 0 : b1 = *utf++;
564 : 0 : b2 = *utf++;
565 : 0 : b3 = *utf++;
566 : 0 : b4 = *utf++;
567 : 0 : }
568 : : else
569 : : {
570 [ # # # # ]: 0 : elog(ERROR, "unsupported character length %d", l);
571 : 0 : iutf = 0; /* keep compiler quiet */
572 : : }
573 : 234 : iutf = (b1 << 24 | b2 << 16 | b3 << 8 | b4);
574 : :
575 : : /* First, try with combined map if possible */
576 [ + + + + ]: 234 : if (cmap && len > l)
577 : : {
578 : 24 : const unsigned char *utf_save = utf;
579 : 24 : int len_save = len;
580 : 24 : int l_save = l;
581 : :
582 : : /* collect next character, same as above */
583 : 24 : len -= l;
584 : :
585 : 24 : l = pg_utf_mblen(utf);
586 [ + + ]: 24 : if (len < l)
587 : : {
588 : : /* need more data to decide if this is a combined char */
589 : 6 : utf -= l_save;
590 : 6 : break;
591 : : }
592 : :
593 [ + - ]: 18 : if (!pg_utf8_islegal(utf, l))
594 : : {
595 [ # # ]: 0 : if (!noError)
596 : 0 : report_invalid_encoding(PG_UTF8, (const char *) utf, len);
597 : 0 : utf -= l_save;
598 : 0 : break;
599 : : }
600 : :
601 : : /* We assume ASCII character cannot be in combined map */
602 [ - + ]: 18 : if (l > 1)
603 : : {
604 : 18 : uint32 iutf2;
605 : 18 : uint32 cutf[2];
606 : :
607 [ + + ]: 18 : if (l == 2)
608 : : {
609 : 9 : iutf2 = *utf++ << 8;
610 : 9 : iutf2 |= *utf++;
611 : 9 : }
612 [ + - ]: 9 : else if (l == 3)
613 : : {
614 : 9 : iutf2 = *utf++ << 16;
615 : 9 : iutf2 |= *utf++ << 8;
616 : 9 : iutf2 |= *utf++;
617 : 9 : }
618 [ # # ]: 0 : else if (l == 4)
619 : : {
620 : 0 : iutf2 = *utf++ << 24;
621 : 0 : iutf2 |= *utf++ << 16;
622 : 0 : iutf2 |= *utf++ << 8;
623 : 0 : iutf2 |= *utf++;
624 : 0 : }
625 : : else
626 : : {
627 [ # # # # ]: 0 : elog(ERROR, "unsupported character length %d", l);
628 : 0 : iutf2 = 0; /* keep compiler quiet */
629 : : }
630 : :
631 : 18 : cutf[0] = iutf;
632 : 18 : cutf[1] = iutf2;
633 : :
634 : 18 : cp = bsearch(cutf, cmap, cmapsize,
635 : : sizeof(pg_utf_to_local_combined), compare3);
636 : :
637 [ + + ]: 18 : if (cp)
638 : : {
639 : 3 : iso = store_coded_char(iso, cp->code);
640 : 3 : continue;
641 : : }
642 [ + + ]: 18 : }
643 : :
644 : : /* fail, so back up to reprocess second character next time */
645 : 15 : utf = utf_save;
646 : 15 : len = len_save;
647 : 15 : l = l_save;
648 [ + + ]: 24 : }
649 : :
650 : : /* Now check ordinary map */
651 [ - + ]: 225 : if (map)
652 : : {
653 : 225 : uint32 converted = pg_mb_radix_conv(map, l, b1, b2, b3, b4);
654 : :
655 [ + + ]: 225 : if (converted)
656 : : {
657 : 75 : iso = store_coded_char(iso, converted);
658 : 75 : continue;
659 : : }
660 [ + + ]: 225 : }
661 : :
662 : : /* if there's a conversion function, try that */
663 [ + + ]: 150 : if (conv_func)
664 : : {
665 : 12 : uint32 converted = (*conv_func) (iutf);
666 : :
667 [ - + ]: 12 : if (converted)
668 : : {
669 : 12 : iso = store_coded_char(iso, converted);
670 : 12 : continue;
671 : : }
672 [ + - ]: 12 : }
673 : :
674 : : /* failed to translate this character */
675 : 138 : utf -= l;
676 [ + + ]: 138 : if (noError)
677 : 69 : break;
678 : 138 : report_untranslatable_char(PG_UTF8, encoding,
679 : 69 : (const char *) utf, len);
680 [ - + + ]: 999 : }
681 : :
682 : : /* if we broke out of loop early, must be invalid input */
683 [ + + + + ]: 309 : if (len > 0 && !noError)
684 : 66 : report_invalid_encoding(PG_UTF8, (const char *) utf, len);
685 : :
686 : 243 : *iso = '\0';
687 : :
688 : 486 : return utf - start;
689 : 243 : }
690 : :
691 : : /*
692 : : * local code ---> UTF8
693 : : *
694 : : * iso: input string in local encoding (need not be null-terminated)
695 : : * len: length of input string (in bytes)
696 : : * utf: pointer to the output area (must be large enough!)
697 : : * (output string will be null-terminated)
698 : : * map: conversion map for single characters
699 : : * cmap: conversion map for combined characters
700 : : * (optional, pass NULL if none)
701 : : * cmapsize: number of entries in the conversion map for combined characters
702 : : * (optional, pass 0 if none)
703 : : * conv_func: algorithmic encoding conversion function
704 : : * (optional, pass NULL if none)
705 : : * encoding: PG identifier for the local encoding
706 : : *
707 : : * For each character, the map is consulted first; if no match, the cmap
708 : : * (if provided) is consulted next; if still no match, the conv_func
709 : : * (if provided) is applied. An error is raised if no match is found.
710 : : *
711 : : * See pg_wchar.h for more details about the data structures used here.
712 : : *
713 : : * Returns the number of input bytes consumed. If noError is true, this can
714 : : * be less than 'len'.
715 : : */
716 : : int
717 : 223 : LocalToUtf(const unsigned char *iso, int len,
718 : : unsigned char *utf,
719 : : const pg_mb_radix_tree *map,
720 : : const pg_local_to_utf_combined *cmap, int cmapsize,
721 : : utf_local_conversion_func conv_func,
722 : : int encoding,
723 : : bool noError)
724 : : {
725 : 223 : uint32 iiso;
726 : 223 : int l;
727 : 223 : const pg_local_to_utf_combined *cp;
728 : 223 : const unsigned char *start = iso;
729 : :
730 [ + - ]: 223 : if (!PG_VALID_ENCODING(encoding))
731 [ # # # # ]: 0 : ereport(ERROR,
732 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
733 : : errmsg("invalid encoding number: %d", encoding)));
734 : :
735 [ + + ]: 1012 : for (; len > 0; len -= l)
736 : : {
737 : 927 : unsigned char b1 = 0;
738 : 927 : unsigned char b2 = 0;
739 : 927 : unsigned char b3 = 0;
740 : 927 : unsigned char b4 = 0;
741 : :
742 : : /* "break" cases all represent errors */
743 [ + + ]: 927 : if (*iso == '\0')
744 : 54 : break;
745 : :
746 [ + + ]: 873 : if (!IS_HIGHBIT_SET(*iso))
747 : : {
748 : : /* ASCII case is easy, assume it's one-to-one conversion */
749 : 693 : *utf++ = *iso++;
750 : 693 : l = 1;
751 : 693 : continue;
752 : : }
753 : :
754 : 180 : l = pg_encoding_verifymbchar(encoding, (const char *) iso, len);
755 [ + + ]: 180 : if (l < 0)
756 : 72 : break;
757 : :
758 : : /* collect coded char of length l */
759 [ + + ]: 108 : if (l == 1)
760 : 33 : b4 = *iso++;
761 [ + + ]: 75 : else if (l == 2)
762 : : {
763 : 60 : b3 = *iso++;
764 : 60 : b4 = *iso++;
765 : 60 : }
766 [ - + ]: 15 : else if (l == 3)
767 : : {
768 : 0 : b2 = *iso++;
769 : 0 : b3 = *iso++;
770 : 0 : b4 = *iso++;
771 : 0 : }
772 [ + - ]: 15 : else if (l == 4)
773 : : {
774 : 15 : b1 = *iso++;
775 : 15 : b2 = *iso++;
776 : 15 : b3 = *iso++;
777 : 15 : b4 = *iso++;
778 : 15 : }
779 : : else
780 : : {
781 [ # # # # ]: 0 : elog(ERROR, "unsupported character length %d", l);
782 : 0 : iiso = 0; /* keep compiler quiet */
783 : : }
784 : 108 : iiso = (b1 << 24 | b2 << 16 | b3 << 8 | b4);
785 : :
786 [ - + ]: 108 : if (map)
787 : : {
788 : 108 : uint32 converted = pg_mb_radix_conv(map, l, b1, b2, b3, b4);
789 : :
790 [ + + ]: 108 : if (converted)
791 : : {
792 : 81 : utf = store_coded_char(utf, converted);
793 : 81 : continue;
794 : : }
795 : :
796 : : /* If there's a combined character map, try that */
797 [ + + ]: 27 : if (cmap)
798 : : {
799 : 6 : cp = bsearch(&iiso, cmap, cmapsize,
800 : : sizeof(pg_local_to_utf_combined), compare4);
801 : :
802 [ + - ]: 6 : if (cp)
803 : : {
804 : 6 : utf = store_coded_char(utf, cp->utf1);
805 : 6 : utf = store_coded_char(utf, cp->utf2);
806 : 6 : continue;
807 : : }
808 : 0 : }
809 [ + + ]: 108 : }
810 : :
811 : : /* if there's a conversion function, try that */
812 [ + + ]: 21 : if (conv_func)
813 : : {
814 : 15 : uint32 converted = (*conv_func) (iiso);
815 : :
816 [ + + ]: 15 : if (converted)
817 : : {
818 : 9 : utf = store_coded_char(utf, converted);
819 : 9 : continue;
820 : : }
821 [ + + ]: 15 : }
822 : :
823 : : /* failed to translate this character */
824 : 12 : iso -= l;
825 [ + + ]: 12 : if (noError)
826 : 6 : break;
827 : 12 : report_untranslatable_char(encoding, PG_UTF8,
828 : 6 : (const char *) iso, len);
829 [ - + + ]: 921 : }
830 : :
831 : : /* if we broke out of loop early, must be invalid input */
832 [ + + + + ]: 217 : if (len > 0 && !noError)
833 : 62 : report_invalid_encoding(encoding, (const char *) iso, len);
834 : :
835 : 155 : *utf = '\0';
836 : :
837 : 310 : return iso - start;
838 : 155 : }
|