Age Owner Branch data TLA Line data Source code
1 : : /*-------------------------------------------------------------------------
2 : : *
3 : : * wchar.c
4 : : * Functions for working with multibyte characters in various encodings.
5 : : *
6 : : * Portions Copyright (c) 1998-2026, PostgreSQL Global Development Group
7 : : *
8 : : * IDENTIFICATION
9 : : * src/common/wchar.c
10 : : *
11 : : *-------------------------------------------------------------------------
12 : : */
13 : : #include "c.h"
14 : :
15 : : #include <limits.h>
16 : :
17 : : #include "mb/pg_wchar.h"
18 : : #include "utils/ascii.h"
19 : :
20 : :
21 : : /*
22 : : * In today's multibyte encodings other than UTF8, this two-byte sequence
23 : : * ensures pg_encoding_mblen() == 2 && pg_encoding_verifymbstr() == 0.
24 : : *
25 : : * For historical reasons, several verifychar implementations opt to reject
26 : : * this pair specifically. Byte pair range constraints, in encoding
27 : : * originator documentation, always excluded this pair. No core conversion
28 : : * could translate it. However, longstanding verifychar implementations
29 : : * accepted any non-NUL byte. big5_to_euc_tw even translates pairs not
30 : : * valid per encoding originator documentation. To avoid tightening core
31 : : * or non-core conversions in a security patch, we sought this one pair.
32 : : *
33 : : * PQescapeString() historically used spaces for BYTE1; many other values
34 : : * could suffice for BYTE1.
35 : : */
36 : : #define NONUTF8_INVALID_BYTE0 (0x8d)
37 : : #define NONUTF8_INVALID_BYTE1 (' ')
38 : :
39 : :
40 : : /* No error-reporting facility. Ignore incomplete trailing byte sequence. */
41 : : #define MB2CHAR_NEED_AT_LEAST(len, need) if ((len) < (need)) break
42 : :
43 : : /*
44 : : * SQL/ASCII
45 : : */
46 : : static int
6961 tgl@sss.pgh.pa.us 47 :CBC 533 : pg_ascii2wchar_with_len(const unsigned char *from, pg_wchar *to, int len)
48 : : {
9359 bruce@momjian.us 49 : 533 : int cnt = 0;
50 : :
9373 tgl@sss.pgh.pa.us 51 [ + + + - ]: 33908 : while (len > 0 && *from)
52 : : {
10292 bruce@momjian.us 53 : 33375 : *to++ = *from++;
54 : 33375 : len--;
9566 ishii@postgresql.org 55 : 33375 : cnt++;
56 : : }
10292 bruce@momjian.us 57 : 533 : *to = 0;
7621 58 : 533 : return cnt;
59 : : }
60 : :
61 : : static int
10292 62 : 18978 : pg_ascii_mblen(const unsigned char *s)
63 : : {
7621 64 : 18978 : return 1;
65 : : }
66 : :
67 : : static int
8270 ishii@postgresql.org 68 : 17473 : pg_ascii_dsplen(const unsigned char *s)
69 : : {
7573 bruce@momjian.us 70 [ - + ]: 17473 : if (*s == '\0')
7573 bruce@momjian.us 71 :UBC 0 : return 0;
7573 bruce@momjian.us 72 [ + + - + ]:CBC 17473 : if (*s < 0x20 || *s == 0x7f)
73 : 2 : return -1;
74 : :
7621 75 : 17471 : return 1;
76 : : }
77 : :
78 : : /*
79 : : * EUC
80 : : */
81 : : static int
6961 tgl@sss.pgh.pa.us 82 : 32 : pg_euc2wchar_with_len(const unsigned char *from, pg_wchar *to, int len)
83 : : {
9359 bruce@momjian.us 84 : 32 : int cnt = 0;
85 : :
9373 tgl@sss.pgh.pa.us 86 [ + + + - ]: 48 : while (len > 0 && *from)
87 : : {
283 tmunro@postgresql.or 88 [ + + ]: 32 : if (*from == SS2) /* JIS X 0201 (so called "1 byte KANA") */
89 : : {
90 [ + + ]: 8 : MB2CHAR_NEED_AT_LEAST(len, 2);
10292 bruce@momjian.us 91 : 4 : from++;
7621 ishii@postgresql.org 92 : 4 : *to = (SS2 << 8) | *from++;
9373 tgl@sss.pgh.pa.us 93 : 4 : len -= 2;
94 : : }
283 tmunro@postgresql.or 95 [ + + ]: 24 : else if (*from == SS3) /* JIS X 0212 KANJI */
96 : : {
97 [ + + ]: 12 : MB2CHAR_NEED_AT_LEAST(len, 3);
10292 bruce@momjian.us 98 : 4 : from++;
7621 ishii@postgresql.org 99 : 4 : *to = (SS3 << 16) | (*from++ << 8);
100 : 4 : *to |= *from++;
10292 bruce@momjian.us 101 : 4 : len -= 3;
102 : : }
283 tmunro@postgresql.or 103 [ + + ]: 12 : else if (IS_HIGHBIT_SET(*from)) /* JIS X 0208 KANJI */
104 : : {
105 [ + + ]: 8 : MB2CHAR_NEED_AT_LEAST(len, 2);
10292 bruce@momjian.us 106 : 4 : *to = *from++ << 8;
107 : 4 : *to |= *from++;
108 : 4 : len -= 2;
109 : : }
110 : : else /* must be ASCII */
111 : : {
112 : 4 : *to = *from++;
113 : 4 : len--;
114 : : }
115 : 16 : to++;
9566 ishii@postgresql.org 116 : 16 : cnt++;
117 : : }
10292 bruce@momjian.us 118 : 32 : *to = 0;
7621 119 : 32 : return cnt;
120 : : }
121 : :
122 : : static inline int
10292 123 : 156 : pg_euc_mblen(const unsigned char *s)
124 : : {
125 : : int len;
126 : :
127 [ - + ]: 156 : if (*s == SS2)
10292 bruce@momjian.us 128 :UBC 0 : len = 2;
10292 bruce@momjian.us 129 [ - + ]:CBC 156 : else if (*s == SS3)
10292 bruce@momjian.us 130 :UBC 0 : len = 3;
7620 bruce@momjian.us 131 [ + + ]:CBC 156 : else if (IS_HIGHBIT_SET(*s))
10292 132 : 108 : len = 2;
133 : : else
134 : 48 : len = 1;
7621 135 : 156 : return len;
136 : : }
137 : :
138 : : static inline int
8270 ishii@postgresql.org 139 :UBC 0 : pg_euc_dsplen(const unsigned char *s)
140 : : {
141 : : int len;
142 : :
143 [ # # ]: 0 : if (*s == SS2)
144 : 0 : len = 2;
145 [ # # ]: 0 : else if (*s == SS3)
146 : 0 : len = 2;
7620 bruce@momjian.us 147 [ # # ]: 0 : else if (IS_HIGHBIT_SET(*s))
8270 ishii@postgresql.org 148 : 0 : len = 2;
149 : : else
7573 bruce@momjian.us 150 : 0 : len = pg_ascii_dsplen(s);
7621 151 : 0 : return len;
152 : : }
153 : :
154 : : /*
155 : : * EUC_JP
156 : : */
157 : : static int
6961 tgl@sss.pgh.pa.us 158 :CBC 32 : pg_eucjp2wchar_with_len(const unsigned char *from, pg_wchar *to, int len)
159 : : {
7621 bruce@momjian.us 160 : 32 : return pg_euc2wchar_with_len(from, to, len);
161 : : }
162 : :
163 : : static int
10292 164 : 136 : pg_eucjp_mblen(const unsigned char *s)
165 : : {
7621 166 : 136 : return pg_euc_mblen(s);
167 : : }
168 : :
169 : : static int
8270 ishii@postgresql.org 170 :UBC 0 : pg_eucjp_dsplen(const unsigned char *s)
171 : : {
172 : : int len;
173 : :
174 [ # # ]: 0 : if (*s == SS2)
175 : 0 : len = 1;
176 [ # # ]: 0 : else if (*s == SS3)
177 : 0 : len = 2;
7620 bruce@momjian.us 178 [ # # ]: 0 : else if (IS_HIGHBIT_SET(*s))
8270 ishii@postgresql.org 179 : 0 : len = 2;
180 : : else
7573 bruce@momjian.us 181 : 0 : len = pg_ascii_dsplen(s);
7621 182 : 0 : return len;
183 : : }
184 : :
185 : : /*
186 : : * EUC_KR
187 : : */
188 : : static int
6961 tgl@sss.pgh.pa.us 189 : 0 : pg_euckr2wchar_with_len(const unsigned char *from, pg_wchar *to, int len)
190 : : {
7621 bruce@momjian.us 191 : 0 : return pg_euc2wchar_with_len(from, to, len);
192 : : }
193 : :
194 : : static int
10292 bruce@momjian.us 195 :CBC 4 : pg_euckr_mblen(const unsigned char *s)
196 : : {
7621 197 : 4 : return pg_euc_mblen(s);
198 : : }
199 : :
200 : : static int
8270 ishii@postgresql.org 201 :UBC 0 : pg_euckr_dsplen(const unsigned char *s)
202 : : {
7621 bruce@momjian.us 203 : 0 : return pg_euc_dsplen(s);
204 : : }
205 : :
206 : : /*
207 : : * EUC_CN
208 : : *
209 : : */
210 : : static int
6961 tgl@sss.pgh.pa.us 211 :CBC 36 : pg_euccn2wchar_with_len(const unsigned char *from, pg_wchar *to, int len)
212 : : {
9359 bruce@momjian.us 213 : 36 : int cnt = 0;
214 : :
9373 tgl@sss.pgh.pa.us 215 [ + + + - ]: 52 : while (len > 0 && *from)
216 : : {
283 tmunro@postgresql.or 217 [ + + ]: 36 : if (*from == SS2) /* code set 2 (unused?) */
218 : : {
219 [ + + ]: 12 : MB2CHAR_NEED_AT_LEAST(len, 3);
10292 bruce@momjian.us 220 : 4 : from++;
7621 ishii@postgresql.org 221 : 4 : *to = (SS2 << 16) | (*from++ << 8);
222 : 4 : *to |= *from++;
9373 tgl@sss.pgh.pa.us 223 : 4 : len -= 3;
224 : : }
283 tmunro@postgresql.or 225 [ + + ]: 24 : else if (*from == SS3) /* code set 3 (unused ?) */
226 : : {
227 [ + + ]: 12 : MB2CHAR_NEED_AT_LEAST(len, 3);
10292 bruce@momjian.us 228 : 4 : from++;
7621 ishii@postgresql.org 229 : 4 : *to = (SS3 << 16) | (*from++ << 8);
230 : 4 : *to |= *from++;
10292 bruce@momjian.us 231 : 4 : len -= 3;
232 : : }
283 tmunro@postgresql.or 233 [ + + ]: 12 : else if (IS_HIGHBIT_SET(*from)) /* code set 1 */
234 : : {
235 [ + + ]: 8 : MB2CHAR_NEED_AT_LEAST(len, 2);
10292 bruce@momjian.us 236 : 4 : *to = *from++ << 8;
237 : 4 : *to |= *from++;
238 : 4 : len -= 2;
239 : : }
240 : : else
241 : : {
242 : 4 : *to = *from++;
243 : 4 : len--;
244 : : }
245 : 16 : to++;
9566 ishii@postgresql.org 246 : 16 : cnt++;
247 : : }
10292 bruce@momjian.us 248 : 36 : *to = 0;
7621 249 : 36 : return cnt;
250 : : }
251 : :
252 : : /*
253 : : * mbverifychar does not accept SS2 or SS3 (CS2 and CS3 are not defined for
254 : : * EUC_CN), but mb2wchar_with_len does. Tell a coherent story for code that
255 : : * relies on agreement between mb2wchar_with_len and mblen. Invalid text
256 : : * datums (e.g. from shared catalogs) reach this.
257 : : */
258 : : static int
10292 259 : 4 : pg_euccn_mblen(const unsigned char *s)
260 : : {
261 : : int len;
262 : :
273 tmunro@postgresql.or 263 [ - + ]: 4 : if (*s == SS2)
273 tmunro@postgresql.or 264 :UBC 0 : len = 3;
273 tmunro@postgresql.or 265 [ - + ]:CBC 4 : else if (*s == SS3)
273 tmunro@postgresql.or 266 :UBC 0 : len = 3;
273 tmunro@postgresql.or 267 [ + - ]:CBC 4 : else if (IS_HIGHBIT_SET(*s))
10292 bruce@momjian.us 268 : 4 : len = 2;
269 : : else
10292 bruce@momjian.us 270 :UBC 0 : len = 1;
7621 bruce@momjian.us 271 :CBC 4 : return len;
272 : : }
273 : :
274 : : static int
8270 ishii@postgresql.org 275 :UBC 0 : pg_euccn_dsplen(const unsigned char *s)
276 : : {
277 : : int len;
278 : :
7620 bruce@momjian.us 279 [ # # ]: 0 : if (IS_HIGHBIT_SET(*s))
8270 ishii@postgresql.org 280 : 0 : len = 2;
281 : : else
7573 bruce@momjian.us 282 : 0 : len = pg_ascii_dsplen(s);
7621 283 : 0 : return len;
284 : : }
285 : :
286 : : /*
287 : : * EUC_TW
288 : : *
289 : : */
290 : : static int
6961 tgl@sss.pgh.pa.us 291 :CBC 40 : pg_euctw2wchar_with_len(const unsigned char *from, pg_wchar *to, int len)
292 : : {
9359 bruce@momjian.us 293 : 40 : int cnt = 0;
294 : :
9373 tgl@sss.pgh.pa.us 295 [ + + + - ]: 56 : while (len > 0 && *from)
296 : : {
283 tmunro@postgresql.or 297 [ + + ]: 40 : if (*from == SS2) /* code set 2 */
298 : : {
299 [ + + ]: 16 : MB2CHAR_NEED_AT_LEAST(len, 4);
10292 bruce@momjian.us 300 : 4 : from++;
7056 tgl@sss.pgh.pa.us 301 : 4 : *to = (((uint32) SS2) << 24) | (*from++ << 16);
10292 bruce@momjian.us 302 : 4 : *to |= *from++ << 8;
303 : 4 : *to |= *from++;
9373 tgl@sss.pgh.pa.us 304 : 4 : len -= 4;
305 : : }
283 tmunro@postgresql.or 306 [ + + ]: 24 : else if (*from == SS3) /* code set 3 (unused?) */
307 : : {
308 [ + + ]: 12 : MB2CHAR_NEED_AT_LEAST(len, 3);
10292 bruce@momjian.us 309 : 4 : from++;
7621 ishii@postgresql.org 310 : 4 : *to = (SS3 << 16) | (*from++ << 8);
311 : 4 : *to |= *from++;
10292 bruce@momjian.us 312 : 4 : len -= 3;
313 : : }
283 tmunro@postgresql.or 314 [ + + ]: 12 : else if (IS_HIGHBIT_SET(*from)) /* code set 2 */
315 : : {
316 [ + + ]: 8 : MB2CHAR_NEED_AT_LEAST(len, 2);
10292 bruce@momjian.us 317 : 4 : *to = *from++ << 8;
318 : 4 : *to |= *from++;
319 : 4 : len -= 2;
320 : : }
321 : : else
322 : : {
323 : 4 : *to = *from++;
324 : 4 : len--;
325 : : }
326 : 16 : to++;
9566 ishii@postgresql.org 327 : 16 : cnt++;
328 : : }
10292 bruce@momjian.us 329 : 40 : *to = 0;
7621 330 : 40 : return cnt;
331 : : }
332 : :
333 : : static int
10292 334 : 4 : pg_euctw_mblen(const unsigned char *s)
335 : : {
336 : : int len;
337 : :
338 [ - + ]: 4 : if (*s == SS2)
10292 bruce@momjian.us 339 :UBC 0 : len = 4;
10292 bruce@momjian.us 340 [ - + ]:CBC 4 : else if (*s == SS3)
10292 bruce@momjian.us 341 :UBC 0 : len = 3;
7620 bruce@momjian.us 342 [ + - ]:CBC 4 : else if (IS_HIGHBIT_SET(*s))
10292 343 : 4 : len = 2;
344 : : else
7473 tgl@sss.pgh.pa.us 345 :UBC 0 : len = 1;
7621 bruce@momjian.us 346 :CBC 4 : return len;
347 : : }
348 : :
349 : : static int
8270 ishii@postgresql.org 350 :UBC 0 : pg_euctw_dsplen(const unsigned char *s)
351 : : {
352 : : int len;
353 : :
354 [ # # ]: 0 : if (*s == SS2)
355 : 0 : len = 2;
356 [ # # ]: 0 : else if (*s == SS3)
357 : 0 : len = 2;
7620 bruce@momjian.us 358 [ # # ]: 0 : else if (IS_HIGHBIT_SET(*s))
8270 ishii@postgresql.org 359 : 0 : len = 2;
360 : : else
7573 bruce@momjian.us 361 : 0 : len = pg_ascii_dsplen(s);
7621 362 : 0 : return len;
363 : : }
364 : :
365 : : /*
366 : : * Convert pg_wchar to EUC_* encoding.
367 : : */
368 : : static int
5237 rhaas@postgresql.org 369 :CBC 48 : pg_wchar2euc_with_len(const pg_wchar *from, unsigned char *to, int len)
370 : : {
371 : 48 : int cnt = 0;
372 : :
373 [ + + + - ]: 96 : while (len > 0 && *from)
374 : : {
375 : : unsigned char c;
376 : :
5231 tgl@sss.pgh.pa.us 377 [ + + ]: 48 : if ((c = (*from >> 24)))
378 : : {
5237 rhaas@postgresql.org 379 : 4 : *to++ = c;
380 : 4 : *to++ = (*from >> 16) & 0xff;
381 : 4 : *to++ = (*from >> 8) & 0xff;
382 : 4 : *to++ = *from & 0xff;
383 : 4 : cnt += 4;
384 : : }
5231 tgl@sss.pgh.pa.us 385 [ + + ]: 44 : else if ((c = (*from >> 16)))
386 : : {
5237 rhaas@postgresql.org 387 : 16 : *to++ = c;
388 : 16 : *to++ = (*from >> 8) & 0xff;
389 : 16 : *to++ = *from & 0xff;
390 : 16 : cnt += 3;
391 : : }
5231 tgl@sss.pgh.pa.us 392 [ + + ]: 28 : else if ((c = (*from >> 8)))
393 : : {
5237 rhaas@postgresql.org 394 : 16 : *to++ = c;
395 : 16 : *to++ = *from & 0xff;
396 : 16 : cnt += 2;
397 : : }
398 : : else
399 : : {
400 : 12 : *to++ = *from;
401 : 12 : cnt++;
402 : : }
5236 403 : 48 : from++;
5237 404 : 48 : len--;
405 : : }
406 : 48 : *to = 0;
407 : 48 : return cnt;
408 : : }
409 : :
410 : :
411 : : /*
412 : : * JOHAB
413 : : */
414 : : static int
9011 bruce@momjian.us 415 : 16 : pg_johab_mblen(const unsigned char *s)
416 : : {
7621 417 : 16 : return pg_euc_mblen(s);
418 : : }
419 : :
420 : : static int
8270 ishii@postgresql.org 421 :UBC 0 : pg_johab_dsplen(const unsigned char *s)
422 : : {
7621 bruce@momjian.us 423 : 0 : return pg_euc_dsplen(s);
424 : : }
425 : :
426 : : /*
427 : : * convert UTF8 string to pg_wchar (UCS-4)
428 : : */
429 : : static int
9142 bruce@momjian.us 430 :CBC 2271832 : pg_utf2wchar_with_len(const unsigned char *from, pg_wchar *to, int len)
431 : : {
9359 432 : 2271832 : int cnt = 0;
433 : : uint32 c1,
434 : : c2,
435 : : c3,
436 : : c4;
437 : :
9373 tgl@sss.pgh.pa.us 438 [ + + + + ]: 32306718 : while (len > 0 && *from)
439 : : {
7225 440 [ + + ]: 30034914 : if ((*from & 0x80) == 0)
441 : : {
10292 bruce@momjian.us 442 : 30034235 : *to = *from++;
443 : 30034235 : len--;
444 : : }
7225 tgl@sss.pgh.pa.us 445 [ + + ]: 679 : else if ((*from & 0xe0) == 0xc0)
446 : : {
283 tmunro@postgresql.or 447 [ + + ]: 345 : MB2CHAR_NEED_AT_LEAST(len, 2);
10292 bruce@momjian.us 448 : 337 : c1 = *from++ & 0x1f;
449 : 337 : c2 = *from++ & 0x3f;
7225 tgl@sss.pgh.pa.us 450 : 337 : *to = (c1 << 6) | c2;
9373 451 : 337 : len -= 2;
452 : : }
7225 453 [ + + ]: 334 : else if ((*from & 0xf0) == 0xe0)
454 : : {
283 tmunro@postgresql.or 455 [ + + ]: 174 : MB2CHAR_NEED_AT_LEAST(len, 3);
10292 bruce@momjian.us 456 : 166 : c1 = *from++ & 0x0f;
457 : 166 : c2 = *from++ & 0x3f;
458 : 166 : c3 = *from++ & 0x3f;
7225 tgl@sss.pgh.pa.us 459 : 166 : *to = (c1 << 12) | (c2 << 6) | c3;
9373 460 : 166 : len -= 3;
461 : : }
7225 462 [ + + ]: 160 : else if ((*from & 0xf8) == 0xf0)
463 : : {
283 tmunro@postgresql.or 464 [ + + ]: 16 : MB2CHAR_NEED_AT_LEAST(len, 4);
7225 tgl@sss.pgh.pa.us 465 : 4 : c1 = *from++ & 0x07;
466 : 4 : c2 = *from++ & 0x3f;
467 : 4 : c3 = *from++ & 0x3f;
468 : 4 : c4 = *from++ & 0x3f;
469 : 4 : *to = (c1 << 18) | (c2 << 12) | (c3 << 6) | c4;
470 : 4 : len -= 4;
471 : : }
472 : : else
473 : : {
474 : : /* treat a bogus char as length 1; not ours to raise error */
10056 475 : 144 : *to = *from++;
476 : 144 : len--;
477 : : }
10292 bruce@momjian.us 478 : 30034886 : to++;
9566 ishii@postgresql.org 479 : 30034886 : cnt++;
480 : : }
10292 bruce@momjian.us 481 : 2271832 : *to = 0;
7621 482 : 2271832 : return cnt;
483 : : }
484 : :
485 : :
486 : : /*
487 : : * Trivial conversion from pg_wchar to UTF-8.
488 : : */
489 : : static int
5237 rhaas@postgresql.org 490 : 579388 : pg_wchar2utf_with_len(const pg_wchar *from, unsigned char *to, int len)
491 : : {
492 : 579388 : int cnt = 0;
493 : :
494 [ + + + - ]: 8542684 : while (len > 0 && *from)
495 : : {
496 : : int char_len;
497 : :
498 : 7963296 : unicode_to_utf8(*from, to);
499 : 7963296 : char_len = pg_utf_mblen(to);
500 : 7963296 : cnt += char_len;
501 : 7963296 : to += char_len;
5236 502 : 7963296 : from++;
503 : 7963296 : len--;
504 : : }
5237 505 : 579388 : *to = 0;
506 : 579388 : return cnt;
507 : : }
508 : :
509 : : /*
510 : : * Return the byte length of a UTF8 character pointed to by s
511 : : *
512 : : * Note: in the current implementation we do not support UTF8 sequences
513 : : * of more than 4 bytes; hence do NOT return a value larger than 4.
514 : : * We return "1" for any leading byte that is either flat-out illegal or
515 : : * indicates a length larger than we support.
516 : : *
517 : : * pg_utf2wchar_with_len(), utf8_to_unicode(), pg_utf8_islegal(), and perhaps
518 : : * other places would need to be fixed to change this.
519 : : */
520 : : int
8007 bruce@momjian.us 521 : 111066518 : pg_utf_mblen(const unsigned char *s)
522 : : {
523 : : int len;
524 : :
10292 525 [ + + ]: 111066518 : if ((*s & 0x80) == 0)
526 : 110960900 : len = 1;
527 [ + + ]: 105618 : else if ((*s & 0xe0) == 0xc0)
528 : 8860 : len = 2;
7691 529 [ + + ]: 96758 : else if ((*s & 0xf0) == 0xe0)
530 : 70376 : len = 3;
531 [ + + ]: 26382 : else if ((*s & 0xf8) == 0xf0)
532 : 26267 : len = 4;
533 : : #ifdef NOT_USED
534 : : else if ((*s & 0xfc) == 0xf8)
535 : : len = 5;
536 : : else if ((*s & 0xfe) == 0xfc)
537 : : len = 6;
538 : : #endif
539 : : else
7225 tgl@sss.pgh.pa.us 540 : 115 : len = 1;
7621 bruce@momjian.us 541 : 111066518 : return len;
542 : : }
543 : :
544 : : /*
545 : : * This is an implementation of wcwidth() and wcswidth() as defined in
546 : : * "The Single UNIX Specification, Version 2, The Open Group, 1997"
547 : : * <http://www.unix.org/online.html>
548 : : *
549 : : * Markus Kuhn -- 2001-09-08 -- public domain
550 : : *
551 : : * customised for PostgreSQL
552 : : *
553 : : * original available at : http://www.cl.cam.ac.uk/~mgk25/ucs/wcwidth.c
554 : : */
555 : :
556 : : struct mbinterval
557 : : {
558 : : unsigned int first;
559 : : unsigned int last;
560 : : };
561 : :
562 : : /* auxiliary function for binary search in interval table */
563 : : static int
3424 tgl@sss.pgh.pa.us 564 : 60688718 : mbbisearch(pg_wchar ucs, const struct mbinterval *table, int max)
565 : : {
7573 bruce@momjian.us 566 : 60688718 : int min = 0;
567 : : int mid;
568 : :
569 [ + + - + ]: 60688718 : if (ucs < table[0].first || ucs > table[max].last)
1897 john.naylor@postgres 570 : 60683048 : return 0;
7573 bruce@momjian.us 571 [ + + ]: 49341 : while (max >= min)
572 : : {
573 : 44163 : mid = (min + max) / 2;
574 [ + + ]: 44163 : if (ucs > table[mid].last)
575 : 9759 : min = mid + 1;
576 [ + + ]: 34404 : else if (ucs < table[mid].first)
577 : 33912 : max = mid - 1;
578 : : else
1897 john.naylor@postgres 579 : 492 : return 1;
580 : : }
581 : :
582 : 5178 : return 0;
583 : : }
584 : :
585 : :
586 : : /*
587 : : * The following functions define the column width of an ISO 10646
588 : : * character as follows:
589 : : *
590 : : * - The null character (U+0000) has a column width of 0.
591 : : *
592 : : * - Other C0/C1 control characters and DEL will lead to a return
593 : : * value of -1.
594 : : *
595 : : * - Non-spacing and enclosing combining characters (general
596 : : * category code Mn, Me or Cf in the Unicode database) have a
597 : : * column width of 0.
598 : : *
599 : : * - Spacing characters in the East Asian Wide (W) or East Asian
600 : : * FullWidth (F) category as defined in Unicode Technical
601 : : * Report #11 have a column width of 2.
602 : : *
603 : : * - All remaining characters (including all printable
604 : : * ISO 8859-1 and WGL4 characters, Unicode control characters,
605 : : * etc.) have a column width of 1.
606 : : *
607 : : * This implementation assumes that wchar_t characters are encoded
608 : : * in ISO 10646.
609 : : */
610 : :
611 : : static int
7573 bruce@momjian.us 612 : 30373769 : ucs_wcwidth(pg_wchar ucs)
613 : : {
614 : : #include "common/unicode_nonspacing_table.h"
615 : : #include "common/unicode_east_asian_fw_table.h"
616 : :
617 : : /* test for 8-bit control characters */
618 [ - + ]: 30373769 : if (ucs == 0)
7573 bruce@momjian.us 619 :UBC 0 : return 0;
620 : :
7573 bruce@momjian.us 621 [ + + + + :CBC 30373769 : if (ucs < 0x20 || (ucs >= 0x7f && ucs < 0xa0) || ucs > 0x0010ffff)
+ + - + ]
622 : 29242 : return -1;
623 : :
624 : : /*
625 : : * binary search in table of non-spacing characters
626 : : *
627 : : * XXX: In the official Unicode sources, it is possible for a character to
628 : : * be described as both non-spacing and wide at the same time. As of
629 : : * Unicode 13.0, treating the non-spacing property as the determining
630 : : * factor for display width leads to the correct behavior, so do that
631 : : * search first.
632 : : */
1514 john.naylor@postgres 633 [ + + ]: 30344527 : if (mbbisearch(ucs, nonspacing,
634 : : sizeof(nonspacing) / sizeof(struct mbinterval) - 1))
1897 635 : 336 : return 0;
636 : :
637 : : /* binary search in table of wide characters */
638 [ + + ]: 30344191 : if (mbbisearch(ucs, east_asian_fw,
639 : : sizeof(east_asian_fw) / sizeof(struct mbinterval) - 1))
640 : 156 : return 2;
641 : :
642 : 30344035 : return 1;
643 : : }
644 : :
645 : : static int
8007 bruce@momjian.us 646 : 30373769 : pg_utf_dsplen(const unsigned char *s)
647 : : {
5923 tgl@sss.pgh.pa.us 648 : 30373769 : return ucs_wcwidth(utf8_to_unicode(s));
649 : : }
650 : :
651 : : /*
652 : : * ISO8859-1
653 : : */
654 : : static int
9142 bruce@momjian.us 655 : 468 : pg_latin12wchar_with_len(const unsigned char *from, pg_wchar *to, int len)
656 : : {
9359 657 : 468 : int cnt = 0;
658 : :
9373 tgl@sss.pgh.pa.us 659 [ + + + - ]: 13247 : while (len > 0 && *from)
660 : : {
10292 bruce@momjian.us 661 : 12779 : *to++ = *from++;
9373 tgl@sss.pgh.pa.us 662 : 12779 : len--;
9566 ishii@postgresql.org 663 : 12779 : cnt++;
664 : : }
10292 bruce@momjian.us 665 : 468 : *to = 0;
7621 666 : 468 : return cnt;
667 : : }
668 : :
669 : : /*
670 : : * Trivial conversion from pg_wchar to single byte encoding. Just ignores
671 : : * high bits.
672 : : */
673 : : static int
5237 rhaas@postgresql.org 674 : 93 : pg_wchar2single_with_len(const pg_wchar *from, unsigned char *to, int len)
675 : : {
676 : 93 : int cnt = 0;
677 : :
678 [ + + + - ]: 830 : while (len > 0 && *from)
679 : : {
680 : 737 : *to++ = *from++;
681 : 737 : len--;
682 : 737 : cnt++;
683 : : }
684 : 93 : *to = 0;
685 : 93 : return cnt;
686 : : }
687 : :
688 : : static int
10292 bruce@momjian.us 689 : 3614 : pg_latin1_mblen(const unsigned char *s)
690 : : {
7621 691 : 3614 : return 1;
692 : : }
693 : :
694 : : static int
8270 ishii@postgresql.org 695 : 400 : pg_latin1_dsplen(const unsigned char *s)
696 : : {
7573 bruce@momjian.us 697 : 400 : return pg_ascii_dsplen(s);
698 : : }
699 : :
700 : : /*
701 : : * SJIS
702 : : */
703 : : static int
10292 704 : 1015 : pg_sjis_mblen(const unsigned char *s)
705 : : {
706 : : int len;
707 : :
708 [ + + - + ]: 1015 : if (*s >= 0xa1 && *s <= 0xdf)
7337 bruce@momjian.us 709 :UBC 0 : len = 1; /* 1 byte kana? */
7619 bruce@momjian.us 710 [ + + ]:CBC 1015 : else if (IS_HIGHBIT_SET(*s))
7337 711 : 809 : len = 2; /* kanji? */
712 : : else
713 : 206 : len = 1; /* should be ASCII */
7621 714 : 1015 : return len;
715 : : }
716 : :
717 : : static int
8270 ishii@postgresql.org 718 :UBC 0 : pg_sjis_dsplen(const unsigned char *s)
719 : : {
720 : : int len;
721 : :
722 [ # # # # ]: 0 : if (*s >= 0xa1 && *s <= 0xdf)
7337 bruce@momjian.us 723 : 0 : len = 1; /* 1 byte kana? */
7619 724 [ # # ]: 0 : else if (IS_HIGHBIT_SET(*s))
7337 725 : 0 : len = 2; /* kanji? */
726 : : else
3424 tgl@sss.pgh.pa.us 727 : 0 : len = pg_ascii_dsplen(s); /* should be ASCII */
7621 bruce@momjian.us 728 : 0 : return len;
729 : : }
730 : :
731 : : /*
732 : : * Big5
733 : : */
734 : : static int
10138 bruce@momjian.us 735 :CBC 232 : pg_big5_mblen(const unsigned char *s)
736 : : {
737 : : int len;
738 : :
7619 739 [ + + ]: 232 : if (IS_HIGHBIT_SET(*s))
7337 740 : 208 : len = 2; /* kanji? */
741 : : else
742 : 24 : len = 1; /* should be ASCII */
7621 743 : 232 : return len;
744 : : }
745 : :
746 : : static int
8270 ishii@postgresql.org 747 :UBC 0 : pg_big5_dsplen(const unsigned char *s)
748 : : {
749 : : int len;
750 : :
7619 bruce@momjian.us 751 [ # # ]: 0 : if (IS_HIGHBIT_SET(*s))
7337 752 : 0 : len = 2; /* kanji? */
753 : : else
3424 tgl@sss.pgh.pa.us 754 : 0 : len = pg_ascii_dsplen(s); /* should be ASCII */
7621 bruce@momjian.us 755 : 0 : return len;
756 : : }
757 : :
758 : : /*
759 : : * GBK
760 : : */
761 : : static int
9011 bruce@momjian.us 762 :CBC 282 : pg_gbk_mblen(const unsigned char *s)
763 : : {
764 : : int len;
765 : :
7619 766 [ + + ]: 282 : if (IS_HIGHBIT_SET(*s))
7337 767 : 212 : len = 2; /* kanji? */
768 : : else
769 : 70 : len = 1; /* should be ASCII */
7621 770 : 282 : return len;
771 : : }
772 : :
773 : : static int
8270 ishii@postgresql.org 774 :UBC 0 : pg_gbk_dsplen(const unsigned char *s)
775 : : {
776 : : int len;
777 : :
7619 bruce@momjian.us 778 [ # # ]: 0 : if (IS_HIGHBIT_SET(*s))
7337 779 : 0 : len = 2; /* kanji? */
780 : : else
3424 tgl@sss.pgh.pa.us 781 : 0 : len = pg_ascii_dsplen(s); /* should be ASCII */
7621 bruce@momjian.us 782 : 0 : return len;
783 : : }
784 : :
785 : : /*
786 : : * UHC
787 : : */
788 : : static int
9011 bruce@momjian.us 789 :CBC 16 : pg_uhc_mblen(const unsigned char *s)
790 : : {
791 : : int len;
792 : :
7619 793 [ + - ]: 16 : if (IS_HIGHBIT_SET(*s))
7337 794 : 16 : len = 2; /* 2byte? */
795 : : else
7337 bruce@momjian.us 796 :UBC 0 : len = 1; /* should be ASCII */
7621 bruce@momjian.us 797 :CBC 16 : return len;
798 : : }
799 : :
800 : : static int
8270 ishii@postgresql.org 801 :UBC 0 : pg_uhc_dsplen(const unsigned char *s)
802 : : {
803 : : int len;
804 : :
7619 bruce@momjian.us 805 [ # # ]: 0 : if (IS_HIGHBIT_SET(*s))
7337 806 : 0 : len = 2; /* 2byte? */
807 : : else
3424 tgl@sss.pgh.pa.us 808 : 0 : len = pg_ascii_dsplen(s); /* should be ASCII */
7621 bruce@momjian.us 809 : 0 : return len;
810 : : }
811 : :
812 : : /*
813 : : * GB18030
814 : : * Added by Bill Huang <bhuang@redhat.com>,<bill_huanghb@ybb.ne.jp>
815 : : */
816 : :
817 : : /*
818 : : * Unlike all other mblen() functions, this also looks at the second byte of
819 : : * the input. However, if you only pass the first byte of a multi-byte
820 : : * string, and \0 as the second byte, this still works in a predictable way:
821 : : * a 4-byte character will be reported as two 2-byte characters. That's
822 : : * enough for all current uses, as a client-only encoding. It works that
823 : : * way, because in any valid 4-byte GB18030-encoded character, the third and
824 : : * fourth byte look like a 2-byte encoded character, when looked at
825 : : * separately.
826 : : */
827 : : static int
8911 ishii@postgresql.org 828 :CBC 623 : pg_gb18030_mblen(const unsigned char *s)
829 : : {
830 : : int len;
831 : :
7619 bruce@momjian.us 832 [ + + ]: 623 : if (!IS_HIGHBIT_SET(*s))
7337 833 : 348 : len = 1; /* ASCII */
4192 tgl@sss.pgh.pa.us 834 [ + + + + ]: 275 : else if (*(s + 1) >= 0x30 && *(s + 1) <= 0x39)
835 : 114 : len = 4;
836 : : else
837 : 161 : len = 2;
7621 bruce@momjian.us 838 : 623 : return len;
839 : : }
840 : :
841 : : static int
8270 ishii@postgresql.org 842 :UBC 0 : pg_gb18030_dsplen(const unsigned char *s)
843 : : {
844 : : int len;
845 : :
7573 bruce@momjian.us 846 [ # # ]: 0 : if (IS_HIGHBIT_SET(*s))
8270 ishii@postgresql.org 847 : 0 : len = 2;
848 : : else
3424 tgl@sss.pgh.pa.us 849 : 0 : len = pg_ascii_dsplen(s); /* ASCII */
7621 bruce@momjian.us 850 : 0 : return len;
851 : : }
852 : :
853 : : /*
854 : : *-------------------------------------------------------------------
855 : : * multibyte sequence validators
856 : : *-------------------------------------------------------------------
857 : : */
858 : :
859 : : static int
2107 heikki.linnakangas@i 860 :CBC 175 : pg_ascii_verifychar(const unsigned char *s, int len)
861 : : {
7473 tgl@sss.pgh.pa.us 862 : 175 : return 1;
863 : : }
864 : :
865 : : static int
2107 heikki.linnakangas@i 866 : 211619 : pg_ascii_verifystr(const unsigned char *s, int len)
867 : : {
868 : 211619 : const unsigned char *nullpos = memchr(s, 0, len);
869 : :
870 [ + - ]: 211619 : if (nullpos == NULL)
871 : 211619 : return len;
872 : : else
2107 heikki.linnakangas@i 873 :UBC 0 : return nullpos - s;
874 : : }
875 : :
876 : : #define IS_EUC_RANGE_VALID(c) ((c) >= 0xa1 && (c) <= 0xfe)
877 : :
878 : : static int
2107 heikki.linnakangas@i 879 :CBC 336 : pg_eucjp_verifychar(const unsigned char *s, int len)
880 : : {
881 : : int l;
882 : : unsigned char c1,
883 : : c2;
884 : :
7473 tgl@sss.pgh.pa.us 885 : 336 : c1 = *s++;
886 : :
887 [ - - + ]: 336 : switch (c1)
888 : : {
7337 bruce@momjian.us 889 :UBC 0 : case SS2: /* JIS X 0201 */
7473 tgl@sss.pgh.pa.us 890 : 0 : l = 2;
891 [ # # ]: 0 : if (l > len)
892 : 0 : return -1;
893 : 0 : c2 = *s++;
894 [ # # # # ]: 0 : if (c2 < 0xa1 || c2 > 0xdf)
895 : 0 : return -1;
896 : 0 : break;
897 : :
7337 bruce@momjian.us 898 : 0 : case SS3: /* JIS X 0212 */
7473 tgl@sss.pgh.pa.us 899 : 0 : l = 3;
900 [ # # ]: 0 : if (l > len)
901 : 0 : return -1;
902 : 0 : c2 = *s++;
903 [ # # # # ]: 0 : if (!IS_EUC_RANGE_VALID(c2))
904 : 0 : return -1;
905 : 0 : c2 = *s++;
906 [ # # # # ]: 0 : if (!IS_EUC_RANGE_VALID(c2))
907 : 0 : return -1;
908 : 0 : break;
909 : :
7473 tgl@sss.pgh.pa.us 910 :CBC 336 : default:
3424 911 [ + - ]: 336 : if (IS_HIGHBIT_SET(c1)) /* JIS X 0208? */
912 : : {
7473 913 : 336 : l = 2;
914 [ + + ]: 336 : if (l > len)
915 : 56 : return -1;
916 [ + + - + ]: 280 : if (!IS_EUC_RANGE_VALID(c1))
917 : 16 : return -1;
918 : 264 : c2 = *s++;
919 [ + + - + ]: 264 : if (!IS_EUC_RANGE_VALID(c2))
920 : 120 : return -1;
921 : : }
922 : : else
923 : : /* must be ASCII */
924 : : {
7473 tgl@sss.pgh.pa.us 925 :UBC 0 : l = 1;
926 : : }
7473 tgl@sss.pgh.pa.us 927 :CBC 144 : break;
928 : : }
929 : :
930 : 144 : return l;
931 : : }
932 : :
933 : : static int
2107 heikki.linnakangas@i 934 : 196 : pg_eucjp_verifystr(const unsigned char *s, int len)
935 : : {
936 : 196 : const unsigned char *start = s;
937 : :
938 [ + + ]: 604 : while (len > 0)
939 : : {
940 : : int l;
941 : :
942 : : /* fast path for ASCII-subset characters */
943 [ + + ]: 552 : if (!IS_HIGHBIT_SET(*s))
944 : : {
945 [ + + ]: 384 : if (*s == '\0')
946 : 48 : break;
947 : 336 : l = 1;
948 : : }
949 : : else
950 : : {
951 : 168 : l = pg_eucjp_verifychar(s, len);
952 [ + + ]: 168 : if (l == -1)
953 : 96 : break;
954 : : }
955 : 408 : s += l;
956 : 408 : len -= l;
957 : : }
958 : :
959 : 196 : return s - start;
960 : : }
961 : :
962 : : static int
963 : 96 : pg_euckr_verifychar(const unsigned char *s, int len)
964 : : {
965 : : int l;
966 : : unsigned char c1,
967 : : c2;
968 : :
7473 tgl@sss.pgh.pa.us 969 : 96 : c1 = *s++;
970 : :
971 [ + - ]: 96 : if (IS_HIGHBIT_SET(c1))
972 : : {
973 : 96 : l = 2;
974 [ + + ]: 96 : if (l > len)
975 : 8 : return -1;
976 [ + + - + ]: 88 : if (!IS_EUC_RANGE_VALID(c1))
977 : 16 : return -1;
978 : 72 : c2 = *s++;
979 [ + - - + ]: 72 : if (!IS_EUC_RANGE_VALID(c2))
7473 tgl@sss.pgh.pa.us 980 :UBC 0 : return -1;
981 : : }
982 : : else
983 : : /* must be ASCII */
984 : : {
985 : 0 : l = 1;
986 : : }
987 : :
7473 tgl@sss.pgh.pa.us 988 :CBC 72 : return l;
989 : : }
990 : :
991 : : static int
2107 heikki.linnakangas@i 992 : 40 : pg_euckr_verifystr(const unsigned char *s, int len)
993 : : {
994 : 40 : const unsigned char *start = s;
995 : :
996 [ + + ]: 124 : while (len > 0)
997 : : {
998 : : int l;
999 : :
1000 : : /* fast path for ASCII-subset characters */
1001 [ + + ]: 108 : if (!IS_HIGHBIT_SET(*s))
1002 : : {
1003 [ - + ]: 48 : if (*s == '\0')
2107 heikki.linnakangas@i 1004 :UBC 0 : break;
2107 heikki.linnakangas@i 1005 :CBC 48 : l = 1;
1006 : : }
1007 : : else
1008 : : {
1009 : 60 : l = pg_euckr_verifychar(s, len);
1010 [ + + ]: 60 : if (l == -1)
1011 : 24 : break;
1012 : : }
1013 : 84 : s += l;
1014 : 84 : len -= l;
1015 : : }
1016 : :
1017 : 40 : return s - start;
1018 : : }
1019 : :
1020 : : /* EUC-CN byte sequences are exactly same as EUC-KR */
1021 : : #define pg_euccn_verifychar pg_euckr_verifychar
1022 : : #define pg_euccn_verifystr pg_euckr_verifystr
1023 : :
1024 : : static int
1025 : 12 : pg_euctw_verifychar(const unsigned char *s, int len)
1026 : : {
1027 : : int l;
1028 : : unsigned char c1,
1029 : : c2;
1030 : :
7473 tgl@sss.pgh.pa.us 1031 : 12 : c1 = *s++;
1032 : :
1033 [ - - + ]: 12 : switch (c1)
1034 : : {
7337 bruce@momjian.us 1035 :UBC 0 : case SS2: /* CNS 11643 Plane 1-7 */
7473 tgl@sss.pgh.pa.us 1036 : 0 : l = 4;
1037 [ # # ]: 0 : if (l > len)
1038 : 0 : return -1;
1039 : 0 : c2 = *s++;
1040 [ # # # # ]: 0 : if (c2 < 0xa1 || c2 > 0xa7)
1041 : 0 : return -1;
1042 : 0 : c2 = *s++;
1043 [ # # # # ]: 0 : if (!IS_EUC_RANGE_VALID(c2))
1044 : 0 : return -1;
1045 : 0 : c2 = *s++;
1046 [ # # # # ]: 0 : if (!IS_EUC_RANGE_VALID(c2))
1047 : 0 : return -1;
1048 : 0 : break;
1049 : :
7337 bruce@momjian.us 1050 : 0 : case SS3: /* unused */
7473 tgl@sss.pgh.pa.us 1051 : 0 : return -1;
1052 : :
7473 tgl@sss.pgh.pa.us 1053 :CBC 12 : default:
3424 1054 [ + - ]: 12 : if (IS_HIGHBIT_SET(c1)) /* CNS 11643 Plane 1 */
1055 : : {
7473 1056 : 12 : l = 2;
1057 [ + + ]: 12 : if (l > len)
1058 : 4 : return -1;
1059 : : /* no further range check on c1? */
1060 : 8 : c2 = *s++;
1061 [ - + - - ]: 8 : if (!IS_EUC_RANGE_VALID(c2))
1062 : 8 : return -1;
1063 : : }
1064 : : else
1065 : : /* must be ASCII */
1066 : : {
7473 tgl@sss.pgh.pa.us 1067 :UBC 0 : l = 1;
1068 : : }
1069 : 0 : break;
1070 : : }
1071 : 0 : return l;
1072 : : }
1073 : :
1074 : : static int
2107 heikki.linnakangas@i 1075 :CBC 20 : pg_euctw_verifystr(const unsigned char *s, int len)
1076 : : {
1077 : 20 : const unsigned char *start = s;
1078 : :
1079 [ + + ]: 44 : while (len > 0)
1080 : : {
1081 : : int l;
1082 : :
1083 : : /* fast path for ASCII-subset characters */
1084 [ + + ]: 36 : if (!IS_HIGHBIT_SET(*s))
1085 : : {
1086 [ - + ]: 24 : if (*s == '\0')
2107 heikki.linnakangas@i 1087 :UBC 0 : break;
2107 heikki.linnakangas@i 1088 :CBC 24 : l = 1;
1089 : : }
1090 : : else
1091 : : {
1092 : 12 : l = pg_euctw_verifychar(s, len);
1093 [ + - ]: 12 : if (l == -1)
1094 : 12 : break;
1095 : : }
1096 : 24 : s += l;
1097 : 24 : len -= l;
1098 : : }
1099 : :
1100 : 20 : return s - start;
1101 : : }
1102 : :
1103 : : static int
1104 : 12 : pg_johab_verifychar(const unsigned char *s, int len)
1105 : : {
1106 : : int l,
1107 : : mbl;
1108 : : unsigned char c;
1109 : :
7473 tgl@sss.pgh.pa.us 1110 : 12 : l = mbl = pg_johab_mblen(s);
1111 : :
1112 [ + + ]: 12 : if (len < l)
1113 : 4 : return -1;
1114 : :
1115 [ - + ]: 8 : if (!IS_HIGHBIT_SET(*s))
7473 tgl@sss.pgh.pa.us 1116 :UBC 0 : return mbl;
1117 : :
7473 tgl@sss.pgh.pa.us 1118 [ + - ]:CBC 8 : while (--l > 0)
1119 : : {
1120 : 8 : c = *++s;
1121 [ - + - - ]: 8 : if (!IS_EUC_RANGE_VALID(c))
1122 : 8 : return -1;
1123 : : }
7473 tgl@sss.pgh.pa.us 1124 :UBC 0 : return mbl;
1125 : : }
1126 : :
1127 : : static int
2107 heikki.linnakangas@i 1128 :CBC 16 : pg_johab_verifystr(const unsigned char *s, int len)
1129 : : {
1130 : 16 : const unsigned char *start = s;
1131 : :
1132 [ + + ]: 28 : while (len > 0)
1133 : : {
1134 : : int l;
1135 : :
1136 : : /* fast path for ASCII-subset characters */
1137 [ + + ]: 24 : if (!IS_HIGHBIT_SET(*s))
1138 : : {
1139 [ - + ]: 12 : if (*s == '\0')
2107 heikki.linnakangas@i 1140 :UBC 0 : break;
2107 heikki.linnakangas@i 1141 :CBC 12 : l = 1;
1142 : : }
1143 : : else
1144 : : {
1145 : 12 : l = pg_johab_verifychar(s, len);
1146 [ + - ]: 12 : if (l == -1)
1147 : 12 : break;
1148 : : }
1149 : 12 : s += l;
1150 : 12 : len -= l;
1151 : : }
1152 : :
1153 : 16 : return s - start;
1154 : : }
1155 : :
1156 : : static int
1157 : 3223 : pg_latin1_verifychar(const unsigned char *s, int len)
1158 : : {
7473 tgl@sss.pgh.pa.us 1159 : 3223 : return 1;
1160 : : }
1161 : :
1162 : : static int
2107 heikki.linnakangas@i 1163 : 5040 : pg_latin1_verifystr(const unsigned char *s, int len)
1164 : : {
1165 : 5040 : const unsigned char *nullpos = memchr(s, 0, len);
1166 : :
1167 [ + + ]: 5040 : if (nullpos == NULL)
1168 : 4968 : return len;
1169 : : else
1170 : 72 : return nullpos - s;
1171 : : }
1172 : :
1173 : : static int
1174 : 624 : pg_sjis_verifychar(const unsigned char *s, int len)
1175 : : {
1176 : : int l,
1177 : : mbl;
1178 : : unsigned char c1,
1179 : : c2;
1180 : :
7473 tgl@sss.pgh.pa.us 1181 : 624 : l = mbl = pg_sjis_mblen(s);
1182 : :
1183 [ + + ]: 624 : if (len < l)
1184 : 86 : return -1;
1185 : :
1186 [ - + ]: 538 : if (l == 1) /* pg_sjis_mblen already verified it */
7473 tgl@sss.pgh.pa.us 1187 :UBC 0 : return mbl;
1188 : :
7473 tgl@sss.pgh.pa.us 1189 :CBC 538 : c1 = *s++;
1190 : 538 : c2 = *s;
1191 [ + - + + : 538 : if (!ISSJISHEAD(c1) || !ISSJISTAIL(c2))
+ - + - +
+ + + + +
- + ]
1192 : 214 : return -1;
1193 : 324 : return mbl;
1194 : : }
1195 : :
1196 : : static int
2107 heikki.linnakangas@i 1197 : 322 : pg_sjis_verifystr(const unsigned char *s, int len)
1198 : : {
1199 : 322 : const unsigned char *start = s;
1200 : :
1201 [ + + ]: 1233 : while (len > 0)
1202 : : {
1203 : : int l;
1204 : :
1205 : : /* fast path for ASCII-subset characters */
1206 [ + + ]: 1107 : if (!IS_HIGHBIT_SET(*s))
1207 : : {
1208 [ + + ]: 815 : if (*s == '\0')
1209 : 48 : break;
1210 : 767 : l = 1;
1211 : : }
1212 : : else
1213 : : {
1214 : 292 : l = pg_sjis_verifychar(s, len);
1215 [ + + ]: 292 : if (l == -1)
1216 : 148 : break;
1217 : : }
1218 : 911 : s += l;
1219 : 911 : len -= l;
1220 : : }
1221 : :
1222 : 322 : return s - start;
1223 : : }
1224 : :
1225 : : static int
1226 : 168 : pg_big5_verifychar(const unsigned char *s, int len)
1227 : : {
1228 : : int l,
1229 : : mbl;
1230 : :
7473 tgl@sss.pgh.pa.us 1231 : 168 : l = mbl = pg_big5_mblen(s);
1232 : :
1233 [ + + ]: 168 : if (len < l)
1234 : 4 : return -1;
1235 : :
633 andres@anarazel.de 1236 [ + - ]: 164 : if (l == 2 &&
1237 [ + + ]: 164 : s[0] == NONUTF8_INVALID_BYTE0 &&
1238 [ + - ]: 8 : s[1] == NONUTF8_INVALID_BYTE1)
1239 : 8 : return -1;
1240 : :
7473 tgl@sss.pgh.pa.us 1241 [ + + ]: 264 : while (--l > 0)
1242 : : {
1243 [ + + ]: 156 : if (*++s == '\0')
1244 : 48 : return -1;
1245 : : }
1246 : :
1247 : 108 : return mbl;
1248 : : }
1249 : :
1250 : : static int
2107 heikki.linnakangas@i 1251 : 104 : pg_big5_verifystr(const unsigned char *s, int len)
1252 : : {
1253 : 104 : const unsigned char *start = s;
1254 : :
1255 [ + + ]: 428 : while (len > 0)
1256 : : {
1257 : : int l;
1258 : :
1259 : : /* fast path for ASCII-subset characters */
1260 [ + + ]: 384 : if (!IS_HIGHBIT_SET(*s))
1261 : : {
1262 [ + + ]: 300 : if (*s == '\0')
1263 : 24 : break;
1264 : 276 : l = 1;
1265 : : }
1266 : : else
1267 : : {
1268 : 84 : l = pg_big5_verifychar(s, len);
1269 [ + + ]: 84 : if (l == -1)
1270 : 36 : break;
1271 : : }
1272 : 324 : s += l;
1273 : 324 : len -= l;
1274 : : }
1275 : :
1276 : 104 : return s - start;
1277 : : }
1278 : :
1279 : : static int
1280 : 140 : pg_gbk_verifychar(const unsigned char *s, int len)
1281 : : {
1282 : : int l,
1283 : : mbl;
1284 : :
7473 tgl@sss.pgh.pa.us 1285 : 140 : l = mbl = pg_gbk_mblen(s);
1286 : :
1287 [ + + ]: 140 : if (len < l)
1288 : 28 : return -1;
1289 : :
633 andres@anarazel.de 1290 [ + - ]: 112 : if (l == 2 &&
1291 [ + + ]: 112 : s[0] == NONUTF8_INVALID_BYTE0 &&
1292 [ + - ]: 16 : s[1] == NONUTF8_INVALID_BYTE1)
1293 : 16 : return -1;
1294 : :
7473 tgl@sss.pgh.pa.us 1295 [ + + ]: 192 : while (--l > 0)
1296 : : {
1297 [ - + ]: 96 : if (*++s == '\0')
7473 tgl@sss.pgh.pa.us 1298 :UBC 0 : return -1;
1299 : : }
1300 : :
7473 tgl@sss.pgh.pa.us 1301 :CBC 96 : return mbl;
1302 : : }
1303 : :
1304 : : static int
2107 heikki.linnakangas@i 1305 : 132 : pg_gbk_verifystr(const unsigned char *s, int len)
1306 : : {
1307 : 132 : const unsigned char *start = s;
1308 : :
1309 [ + + ]: 336 : while (len > 0)
1310 : : {
1311 : : int l;
1312 : :
1313 : : /* fast path for ASCII-subset characters */
1314 [ + + ]: 248 : if (!IS_HIGHBIT_SET(*s))
1315 : : {
1316 [ - + ]: 124 : if (*s == '\0')
2107 heikki.linnakangas@i 1317 :UBC 0 : break;
2107 heikki.linnakangas@i 1318 :CBC 124 : l = 1;
1319 : : }
1320 : : else
1321 : : {
1322 : 124 : l = pg_gbk_verifychar(s, len);
1323 [ + + ]: 124 : if (l == -1)
1324 : 44 : break;
1325 : : }
1326 : 204 : s += l;
1327 : 204 : len -= l;
1328 : : }
1329 : :
1330 : 132 : return s - start;
1331 : : }
1332 : :
1333 : : static int
1334 : 12 : pg_uhc_verifychar(const unsigned char *s, int len)
1335 : : {
1336 : : int l,
1337 : : mbl;
1338 : :
7473 tgl@sss.pgh.pa.us 1339 : 12 : l = mbl = pg_uhc_mblen(s);
1340 : :
1341 [ + + ]: 12 : if (len < l)
1342 : 4 : return -1;
1343 : :
633 andres@anarazel.de 1344 [ + - ]: 8 : if (l == 2 &&
1345 [ + - ]: 8 : s[0] == NONUTF8_INVALID_BYTE0 &&
1346 [ + - ]: 8 : s[1] == NONUTF8_INVALID_BYTE1)
1347 : 8 : return -1;
1348 : :
7473 tgl@sss.pgh.pa.us 1349 [ # # ]:UBC 0 : while (--l > 0)
1350 : : {
1351 [ # # ]: 0 : if (*++s == '\0')
1352 : 0 : return -1;
1353 : : }
1354 : :
1355 : 0 : return mbl;
1356 : : }
1357 : :
1358 : : static int
2107 heikki.linnakangas@i 1359 :CBC 16 : pg_uhc_verifystr(const unsigned char *s, int len)
1360 : : {
1361 : 16 : const unsigned char *start = s;
1362 : :
1363 [ + + ]: 28 : while (len > 0)
1364 : : {
1365 : : int l;
1366 : :
1367 : : /* fast path for ASCII-subset characters */
1368 [ + + ]: 24 : if (!IS_HIGHBIT_SET(*s))
1369 : : {
1370 [ - + ]: 12 : if (*s == '\0')
2107 heikki.linnakangas@i 1371 :UBC 0 : break;
2107 heikki.linnakangas@i 1372 :CBC 12 : l = 1;
1373 : : }
1374 : : else
1375 : : {
1376 : 12 : l = pg_uhc_verifychar(s, len);
1377 [ + - ]: 12 : if (l == -1)
1378 : 12 : break;
1379 : : }
1380 : 12 : s += l;
1381 : 12 : len -= l;
1382 : : }
1383 : :
1384 : 16 : return s - start;
1385 : : }
1386 : :
1387 : : static int
1388 : 698 : pg_gb18030_verifychar(const unsigned char *s, int len)
1389 : : {
1390 : : int l;
1391 : :
4192 tgl@sss.pgh.pa.us 1392 [ - + ]: 698 : if (!IS_HIGHBIT_SET(*s))
4192 tgl@sss.pgh.pa.us 1393 :UBC 0 : l = 1; /* ASCII */
4192 tgl@sss.pgh.pa.us 1394 [ + + + + :CBC 698 : else if (len >= 4 && *(s + 1) >= 0x30 && *(s + 1) <= 0x39)
+ + ]
1395 : : {
1396 : : /* Should be 4-byte, validate remaining bytes */
1397 [ + + + - ]: 210 : if (*s >= 0x81 && *s <= 0xfe &&
1398 [ + - + - ]: 204 : *(s + 2) >= 0x81 && *(s + 2) <= 0xfe &&
1399 [ + + + - ]: 204 : *(s + 3) >= 0x30 && *(s + 3) <= 0x39)
1400 : 108 : l = 4;
1401 : : else
1402 : 102 : l = -1;
1403 : : }
1404 [ + + + + : 488 : else if (len >= 2 && *s >= 0x81 && *s <= 0xfe)
+ - ]
1405 : : {
1406 : : /* Should be 2-byte, validate */
1407 [ + + + + ]: 358 : if ((*(s + 1) >= 0x40 && *(s + 1) <= 0x7e) ||
1408 [ + + + - ]: 238 : (*(s + 1) >= 0x80 && *(s + 1) <= 0xfe))
1409 : 176 : l = 2;
1410 : : else
1411 : 182 : l = -1;
1412 : : }
1413 : : else
1414 : 130 : l = -1;
1415 : 698 : return l;
1416 : : }
1417 : :
1418 : : static int
2107 heikki.linnakangas@i 1419 : 500 : pg_gb18030_verifystr(const unsigned char *s, int len)
1420 : : {
1421 : 500 : const unsigned char *start = s;
1422 : :
1423 [ + + ]: 1679 : while (len > 0)
1424 : : {
1425 : : int l;
1426 : :
1427 : : /* fast path for ASCII-subset characters */
1428 [ + + ]: 1515 : if (!IS_HIGHBIT_SET(*s))
1429 : : {
1430 [ + + ]: 1037 : if (*s == '\0')
1431 : 30 : break;
1432 : 1007 : l = 1;
1433 : : }
1434 : : else
1435 : : {
1436 : 478 : l = pg_gb18030_verifychar(s, len);
1437 [ + + ]: 478 : if (l == -1)
1438 : 306 : break;
1439 : : }
1440 : 1179 : s += l;
1441 : 1179 : len -= l;
1442 : : }
1443 : :
1444 : 500 : return s - start;
1445 : : }
1446 : :
1447 : : static int
1448 : 9452 : pg_utf8_verifychar(const unsigned char *s, int len)
1449 : : {
1450 : : int l;
1451 : :
1452 [ - + ]: 9452 : if ((*s & 0x80) == 0)
1453 : : {
2107 heikki.linnakangas@i 1454 [ # # ]:UBC 0 : if (*s == '\0')
1455 : 0 : return -1;
1456 : 0 : return 1;
1457 : : }
2107 heikki.linnakangas@i 1458 [ + + ]:CBC 9452 : else if ((*s & 0xe0) == 0xc0)
1459 : 3332 : l = 2;
1460 [ + + ]: 6120 : else if ((*s & 0xf0) == 0xe0)
1461 : 3412 : l = 3;
1462 [ + + ]: 2708 : else if ((*s & 0xf8) == 0xf0)
1463 : 2532 : l = 4;
1464 : : else
1465 : 176 : l = 1;
1466 : :
1467 [ + + ]: 9452 : if (l > len)
7473 tgl@sss.pgh.pa.us 1468 : 320 : return -1;
1469 : :
1470 [ + + ]: 9132 : if (!pg_utf8_islegal(s, l))
1471 : 1486 : return -1;
1472 : :
1473 : 7646 : return l;
1474 : : }
1475 : :
1476 : : /*
1477 : : * The fast path of the UTF-8 verifier uses a deterministic finite automaton
1478 : : * (DFA) for multibyte characters. In a traditional table-driven DFA, the
1479 : : * input byte and current state are used to compute an index into an array of
1480 : : * state transitions. Since the address of the next transition is dependent
1481 : : * on this computation, there is latency in executing the load instruction,
1482 : : * and the CPU is not kept busy.
1483 : : *
1484 : : * Instead, we use a "shift-based" DFA as described by Per Vognsen:
1485 : : *
1486 : : * https://gist.github.com/pervognsen/218ea17743e1442e59bb60d29b1aa725
1487 : : *
1488 : : * In a shift-based DFA, the input byte is an index into array of integers
1489 : : * whose bit pattern encodes the state transitions. To compute the next
1490 : : * state, we simply right-shift the integer by the current state and apply a
1491 : : * mask. In this scheme, the address of the transition only depends on the
1492 : : * input byte, so there is better pipelining.
1493 : : *
1494 : : * The naming convention for states and transitions was adopted from a UTF-8
1495 : : * to UTF-16/32 transcoder, whose table is reproduced below:
1496 : : *
1497 : : * https://github.com/BobSteagall/utf_utils/blob/6b7a465265de2f5fa6133d653df0c9bdd73bbcf8/src/utf_utils.cpp
1498 : : *
1499 : : * ILL ASC CR1 CR2 CR3 L2A L3A L3B L3C L4A L4B L4C CLASS / STATE
1500 : : * ==========================================================================
1501 : : * err, END, err, err, err, CS1, P3A, CS2, P3B, P4A, CS3, P4B, | BGN/END
1502 : : * err, err, err, err, err, err, err, err, err, err, err, err, | ERR
1503 : : * |
1504 : : * err, err, END, END, END, err, err, err, err, err, err, err, | CS1
1505 : : * err, err, CS1, CS1, CS1, err, err, err, err, err, err, err, | CS2
1506 : : * err, err, CS2, CS2, CS2, err, err, err, err, err, err, err, | CS3
1507 : : * |
1508 : : * err, err, err, err, CS1, err, err, err, err, err, err, err, | P3A
1509 : : * err, err, CS1, CS1, err, err, err, err, err, err, err, err, | P3B
1510 : : * |
1511 : : * err, err, err, CS2, CS2, err, err, err, err, err, err, err, | P4A
1512 : : * err, err, CS2, err, err, err, err, err, err, err, err, err, | P4B
1513 : : *
1514 : : * In the most straightforward implementation, a shift-based DFA for UTF-8
1515 : : * requires 64-bit integers to encode the transitions, but with an SMT solver
1516 : : * it's possible to find state numbers such that the transitions fit within
1517 : : * 32-bit integers, as Dougall Johnson demonstrated:
1518 : : *
1519 : : * https://gist.github.com/dougallj/166e326de6ad4cf2c94be97a204c025f
1520 : : *
1521 : : * This packed representation is the reason for the seemingly odd choice of
1522 : : * state values below.
1523 : : */
1524 : :
1525 : : /* Error */
1526 : : #define ERR 0
1527 : : /* Begin */
1528 : : #define BGN 11
1529 : : /* Continuation states, expect 1/2/3 continuation bytes */
1530 : : #define CS1 16
1531 : : #define CS2 1
1532 : : #define CS3 5
1533 : : /* Partial states, where the first continuation byte has a restricted range */
1534 : : #define P3A 6 /* Lead was E0, check for 3-byte overlong */
1535 : : #define P3B 20 /* Lead was ED, check for surrogate */
1536 : : #define P4A 25 /* Lead was F0, check for 4-byte overlong */
1537 : : #define P4B 30 /* Lead was F4, check for too-large */
1538 : : /* Begin and End are the same state */
1539 : : #define END BGN
1540 : :
1541 : : /* the encoded state transitions for the lookup table */
1542 : :
1543 : : /* ASCII */
1544 : : #define ASC (END << BGN)
1545 : : /* 2-byte lead */
1546 : : #define L2A (CS1 << BGN)
1547 : : /* 3-byte lead */
1548 : : #define L3A (P3A << BGN)
1549 : : #define L3B (CS2 << BGN)
1550 : : #define L3C (P3B << BGN)
1551 : : /* 4-byte lead */
1552 : : #define L4A (P4A << BGN)
1553 : : #define L4B (CS3 << BGN)
1554 : : #define L4C (P4B << BGN)
1555 : : /* continuation byte */
1556 : : #define CR1 (END << CS1) | (CS1 << CS2) | (CS2 << CS3) | (CS1 << P3B) | (CS2 << P4B)
1557 : : #define CR2 (END << CS1) | (CS1 << CS2) | (CS2 << CS3) | (CS1 << P3B) | (CS2 << P4A)
1558 : : #define CR3 (END << CS1) | (CS1 << CS2) | (CS2 << CS3) | (CS1 << P3A) | (CS2 << P4A)
1559 : : /* invalid byte */
1560 : : #define ILL ERR
1561 : :
1562 : : static const uint32 Utf8Transition[256] =
1563 : : {
1564 : : /* ASCII */
1565 : :
1566 : : ILL, ASC, ASC, ASC, ASC, ASC, ASC, ASC,
1567 : : ASC, ASC, ASC, ASC, ASC, ASC, ASC, ASC,
1568 : : ASC, ASC, ASC, ASC, ASC, ASC, ASC, ASC,
1569 : : ASC, ASC, ASC, ASC, ASC, ASC, ASC, ASC,
1570 : :
1571 : : ASC, ASC, ASC, ASC, ASC, ASC, ASC, ASC,
1572 : : ASC, ASC, ASC, ASC, ASC, ASC, ASC, ASC,
1573 : : ASC, ASC, ASC, ASC, ASC, ASC, ASC, ASC,
1574 : : ASC, ASC, ASC, ASC, ASC, ASC, ASC, ASC,
1575 : :
1576 : : ASC, ASC, ASC, ASC, ASC, ASC, ASC, ASC,
1577 : : ASC, ASC, ASC, ASC, ASC, ASC, ASC, ASC,
1578 : : ASC, ASC, ASC, ASC, ASC, ASC, ASC, ASC,
1579 : : ASC, ASC, ASC, ASC, ASC, ASC, ASC, ASC,
1580 : :
1581 : : ASC, ASC, ASC, ASC, ASC, ASC, ASC, ASC,
1582 : : ASC, ASC, ASC, ASC, ASC, ASC, ASC, ASC,
1583 : : ASC, ASC, ASC, ASC, ASC, ASC, ASC, ASC,
1584 : : ASC, ASC, ASC, ASC, ASC, ASC, ASC, ASC,
1585 : :
1586 : : /* continuation bytes */
1587 : :
1588 : : /* 80..8F */
1589 : : CR1, CR1, CR1, CR1, CR1, CR1, CR1, CR1,
1590 : : CR1, CR1, CR1, CR1, CR1, CR1, CR1, CR1,
1591 : :
1592 : : /* 90..9F */
1593 : : CR2, CR2, CR2, CR2, CR2, CR2, CR2, CR2,
1594 : : CR2, CR2, CR2, CR2, CR2, CR2, CR2, CR2,
1595 : :
1596 : : /* A0..BF */
1597 : : CR3, CR3, CR3, CR3, CR3, CR3, CR3, CR3,
1598 : : CR3, CR3, CR3, CR3, CR3, CR3, CR3, CR3,
1599 : : CR3, CR3, CR3, CR3, CR3, CR3, CR3, CR3,
1600 : : CR3, CR3, CR3, CR3, CR3, CR3, CR3, CR3,
1601 : :
1602 : : /* leading bytes */
1603 : :
1604 : : /* C0..DF */
1605 : : ILL, ILL, L2A, L2A, L2A, L2A, L2A, L2A,
1606 : : L2A, L2A, L2A, L2A, L2A, L2A, L2A, L2A,
1607 : : L2A, L2A, L2A, L2A, L2A, L2A, L2A, L2A,
1608 : : L2A, L2A, L2A, L2A, L2A, L2A, L2A, L2A,
1609 : :
1610 : : /* E0..EF */
1611 : : L3A, L3B, L3B, L3B, L3B, L3B, L3B, L3B,
1612 : : L3B, L3B, L3B, L3B, L3B, L3C, L3B, L3B,
1613 : :
1614 : : /* F0..FF */
1615 : : L4A, L4B, L4B, L4B, L4C, ILL, ILL, ILL,
1616 : : ILL, ILL, ILL, ILL, ILL, ILL, ILL, ILL
1617 : : };
1618 : :
1619 : : static void
1843 john.naylor@postgres 1620 : 1167 : utf8_advance(const unsigned char *s, uint32 *state, int len)
1621 : : {
1622 : : /* Note: We deliberately don't check the state's value here. */
1623 [ + + ]: 38511 : while (len > 0)
1624 : : {
1625 : : /*
1626 : : * It's important that the mask value is 31: In most instruction sets,
1627 : : * a shift by a 32-bit operand is understood to be a shift by its mod
1628 : : * 32, so the compiler should elide the mask operation.
1629 : : */
1630 : 37344 : *state = Utf8Transition[*s++] >> (*state & 31);
1631 : 37344 : len--;
1632 : : }
1633 : :
1634 : 1167 : *state &= 31;
1635 : 1167 : }
1636 : :
1637 : : static int
2107 heikki.linnakangas@i 1638 : 1057414 : pg_utf8_verifystr(const unsigned char *s, int len)
1639 : : {
1640 : 1057414 : const unsigned char *start = s;
1843 john.naylor@postgres 1641 : 1057414 : const int orig_len = len;
1642 : 1057414 : uint32 state = BGN;
1643 : :
1644 : : /*
1645 : : * With a stride of two vector widths, gcc will unroll the loop. Even if
1646 : : * the compiler can unroll a longer loop, it's not worth it because we
1647 : : * must fall back to the byte-wise algorithm if we find any non-ASCII.
1648 : : */
1649 : : #define STRIDE_LENGTH (2 * sizeof(Vector8))
1650 : :
1651 [ + + ]: 1057414 : if (len >= STRIDE_LENGTH)
1652 : : {
1653 [ + + ]: 3446525 : while (len >= STRIDE_LENGTH)
1654 : : {
1655 : : /*
1656 : : * If the chunk is all ASCII, we can skip the full UTF-8 check,
1657 : : * but we must first check for a non-END state, which means the
1658 : : * previous chunk ended in the middle of a multibyte sequence.
1659 : : */
1660 [ + + + + ]: 2787338 : if (state != END || !is_valid_ascii(s, STRIDE_LENGTH))
1661 : 1167 : utf8_advance(s, &state, STRIDE_LENGTH);
1662 : :
1663 : 2787338 : s += STRIDE_LENGTH;
1664 : 2787338 : len -= STRIDE_LENGTH;
1665 : : }
1666 : :
1667 : : /* The error state persists, so we only need to check for it here. */
1668 [ + + ]: 659187 : if (state == ERR)
1669 : : {
1670 : : /*
1671 : : * Start over from the beginning with the slow path so we can
1672 : : * count the valid bytes.
1673 : : */
1674 : 336 : len = orig_len;
1675 : 336 : s = start;
1676 : : }
1753 1677 [ + + ]: 658851 : else if (state != END)
1678 : : {
1679 : : /*
1680 : : * The fast path exited in the middle of a multibyte sequence.
1681 : : * Walk backwards to find the leading byte so that the slow path
1682 : : * can resume checking from there. We must always backtrack at
1683 : : * least one byte, since the current byte could be e.g. an ASCII
1684 : : * byte after a 2-byte lead, which is invalid.
1685 : : */
1686 : : do
1687 : : {
1688 [ - + ]: 73 : Assert(s > start);
1689 : 73 : s--;
1690 : 73 : len++;
1691 [ - + ]: 73 : Assert(IS_HIGHBIT_SET(*s));
1692 [ + + ]: 73 : } while (pg_utf_mblen(s) <= 1);
1693 : : }
1694 : : }
1695 : :
1696 : : /* check remaining bytes */
2107 heikki.linnakangas@i 1697 [ + + ]: 14416706 : while (len > 0)
1698 : : {
1699 : : int l;
1700 : :
1701 : : /* fast path for ASCII-subset characters */
1702 [ + + ]: 13361198 : if (!IS_HIGHBIT_SET(*s))
1703 : : {
1704 [ + + ]: 13351782 : if (*s == '\0')
1705 : 132 : break;
1706 : 13351650 : l = 1;
1707 : : }
1708 : : else
1709 : : {
1710 : 9416 : l = pg_utf8_verifychar(s, len);
1711 [ + + ]: 9416 : if (l == -1)
1712 : 1774 : break;
1713 : : }
1714 : 13359292 : s += l;
1715 : 13359292 : len -= l;
1716 : : }
1717 : :
1718 : 1057414 : return s - start;
1719 : : }
1720 : :
1721 : : /*
1722 : : * Check for validity of a single UTF-8 encoded character
1723 : : *
1724 : : * This directly implements the rules in RFC3629. The bizarre-looking
1725 : : * restrictions on the second byte are meant to ensure that there isn't
1726 : : * more than one encoding of a given Unicode character point; that is,
1727 : : * you may not use a longer-than-necessary byte sequence with high order
1728 : : * zero bits to represent a character that would fit in fewer bytes.
1729 : : * To do otherwise is to create security hazards (eg, create an apparent
1730 : : * non-ASCII character that decodes to plain ASCII).
1731 : : *
1732 : : * length is assumed to have been obtained by pg_utf_mblen(), and the
1733 : : * caller must have checked that that many bytes are present in the buffer.
1734 : : */
1735 : : bool
7691 bruce@momjian.us 1736 : 16188 : pg_utf8_islegal(const unsigned char *source, int length)
1737 : : {
1738 : : unsigned char a;
1739 : :
1740 [ - + + + : 16188 : switch (length)
+ ]
1741 : : {
7691 bruce@momjian.us 1742 :UBC 0 : default:
1743 : : /* reject lengths 5 and 6 for now */
1744 : 0 : return false;
7691 bruce@momjian.us 1745 :CBC 2396 : case 4:
7473 tgl@sss.pgh.pa.us 1746 : 2396 : a = source[3];
1747 [ + + - + ]: 2396 : if (a < 0x80 || a > 0xBF)
7691 bruce@momjian.us 1748 : 198 : return false;
1749 : : pg_fallthrough;
1750 : : case 3:
7473 tgl@sss.pgh.pa.us 1751 : 6624 : a = source[2];
1752 [ + + + + ]: 6624 : if (a < 0x80 || a > 0xBF)
7691 bruce@momjian.us 1753 : 440 : return false;
1754 : : pg_fallthrough;
1755 : : case 2:
7473 tgl@sss.pgh.pa.us 1756 : 9851 : a = source[1];
7691 bruce@momjian.us 1757 [ + + + + : 9851 : switch (*source)
+ ]
1758 : : {
1759 : 208 : case 0xE0:
7473 tgl@sss.pgh.pa.us 1760 [ + + - + ]: 208 : if (a < 0xA0 || a > 0xBF)
7691 bruce@momjian.us 1761 : 176 : return false;
1762 : 32 : break;
1763 : 208 : case 0xED:
7473 tgl@sss.pgh.pa.us 1764 [ + - + + ]: 208 : if (a < 0x80 || a > 0x9F)
7691 bruce@momjian.us 1765 : 176 : return false;
1766 : 32 : break;
1767 : 2078 : case 0xF0:
7473 tgl@sss.pgh.pa.us 1768 [ + + - + ]: 2078 : if (a < 0x90 || a > 0xBF)
7691 bruce@momjian.us 1769 : 176 : return false;
1770 : 1902 : break;
1771 : 120 : case 0xF4:
7473 tgl@sss.pgh.pa.us 1772 [ + - + + ]: 120 : if (a < 0x80 || a > 0x8F)
7691 bruce@momjian.us 1773 : 88 : return false;
1774 : 32 : break;
1775 : 7237 : default:
7473 tgl@sss.pgh.pa.us 1776 [ + + - + ]: 7237 : if (a < 0x80 || a > 0xBF)
7691 bruce@momjian.us 1777 : 168 : return false;
7473 tgl@sss.pgh.pa.us 1778 : 7069 : break;
1779 : : }
1780 : : pg_fallthrough;
1781 : : case 1:
1782 : 14766 : a = *source;
1783 [ + + + + ]: 14766 : if (a >= 0x80 && a < 0xC2)
1784 : 264 : return false;
1785 [ + + ]: 14502 : if (a > 0xF4)
7691 bruce@momjian.us 1786 : 88 : return false;
7473 tgl@sss.pgh.pa.us 1787 : 14414 : break;
1788 : : }
7691 bruce@momjian.us 1789 : 14414 : return true;
1790 : : }
1791 : :
1792 : :
1793 : : /*
1794 : : * Fills the provided buffer with two bytes such that:
1795 : : * pg_encoding_mblen(dst) == 2 && pg_encoding_verifymbstr(dst) == 0
1796 : : */
1797 : : void
633 andres@anarazel.de 1798 : 212 : pg_encoding_set_invalid(int encoding, char *dst)
1799 : : {
1800 [ - + ]: 212 : Assert(pg_encoding_max_length(encoding) > 1);
1801 : :
1802 [ + + ]: 212 : dst[0] = (encoding == PG_UTF8 ? 0xc0 : NONUTF8_INVALID_BYTE0);
1803 : 212 : dst[1] = NONUTF8_INVALID_BYTE1;
1804 : 212 : }
1805 : :
1806 : : /*
1807 : : *-------------------------------------------------------------------
1808 : : * encoding info table
1809 : : *-------------------------------------------------------------------
1810 : : */
1811 : : const pg_wchar_tbl pg_wchar_table[] = {
1812 : : [PG_SQL_ASCII] = {pg_ascii2wchar_with_len, pg_wchar2single_with_len, pg_ascii_mblen, pg_ascii_dsplen, pg_ascii_verifychar, pg_ascii_verifystr, 1},
1813 : : [PG_EUC_JP] = {pg_eucjp2wchar_with_len, pg_wchar2euc_with_len, pg_eucjp_mblen, pg_eucjp_dsplen, pg_eucjp_verifychar, pg_eucjp_verifystr, 3},
1814 : : [PG_EUC_CN] = {pg_euccn2wchar_with_len, pg_wchar2euc_with_len, pg_euccn_mblen, pg_euccn_dsplen, pg_euccn_verifychar, pg_euccn_verifystr, 3},
1815 : : [PG_EUC_KR] = {pg_euckr2wchar_with_len, pg_wchar2euc_with_len, pg_euckr_mblen, pg_euckr_dsplen, pg_euckr_verifychar, pg_euckr_verifystr, 3},
1816 : : [PG_EUC_TW] = {pg_euctw2wchar_with_len, pg_wchar2euc_with_len, pg_euctw_mblen, pg_euctw_dsplen, pg_euctw_verifychar, pg_euctw_verifystr, 4},
1817 : : [PG_EUC_JIS_2004] = {pg_eucjp2wchar_with_len, pg_wchar2euc_with_len, pg_eucjp_mblen, pg_eucjp_dsplen, pg_eucjp_verifychar, pg_eucjp_verifystr, 3},
1818 : : [PG_UTF8] = {pg_utf2wchar_with_len, pg_wchar2utf_with_len, pg_utf_mblen, pg_utf_dsplen, pg_utf8_verifychar, pg_utf8_verifystr, 4},
1819 : : [PG_LATIN1] = {pg_latin12wchar_with_len, pg_wchar2single_with_len, pg_latin1_mblen, pg_latin1_dsplen, pg_latin1_verifychar, pg_latin1_verifystr, 1},
1820 : : [PG_LATIN2] = {pg_latin12wchar_with_len, pg_wchar2single_with_len, pg_latin1_mblen, pg_latin1_dsplen, pg_latin1_verifychar, pg_latin1_verifystr, 1},
1821 : : [PG_LATIN3] = {pg_latin12wchar_with_len, pg_wchar2single_with_len, pg_latin1_mblen, pg_latin1_dsplen, pg_latin1_verifychar, pg_latin1_verifystr, 1},
1822 : : [PG_LATIN4] = {pg_latin12wchar_with_len, pg_wchar2single_with_len, pg_latin1_mblen, pg_latin1_dsplen, pg_latin1_verifychar, pg_latin1_verifystr, 1},
1823 : : [PG_LATIN5] = {pg_latin12wchar_with_len, pg_wchar2single_with_len, pg_latin1_mblen, pg_latin1_dsplen, pg_latin1_verifychar, pg_latin1_verifystr, 1},
1824 : : [PG_LATIN6] = {pg_latin12wchar_with_len, pg_wchar2single_with_len, pg_latin1_mblen, pg_latin1_dsplen, pg_latin1_verifychar, pg_latin1_verifystr, 1},
1825 : : [PG_LATIN7] = {pg_latin12wchar_with_len, pg_wchar2single_with_len, pg_latin1_mblen, pg_latin1_dsplen, pg_latin1_verifychar, pg_latin1_verifystr, 1},
1826 : : [PG_LATIN8] = {pg_latin12wchar_with_len, pg_wchar2single_with_len, pg_latin1_mblen, pg_latin1_dsplen, pg_latin1_verifychar, pg_latin1_verifystr, 1},
1827 : : [PG_LATIN9] = {pg_latin12wchar_with_len, pg_wchar2single_with_len, pg_latin1_mblen, pg_latin1_dsplen, pg_latin1_verifychar, pg_latin1_verifystr, 1},
1828 : : [PG_LATIN10] = {pg_latin12wchar_with_len, pg_wchar2single_with_len, pg_latin1_mblen, pg_latin1_dsplen, pg_latin1_verifychar, pg_latin1_verifystr, 1},
1829 : : [PG_WIN1256] = {pg_latin12wchar_with_len, pg_wchar2single_with_len, pg_latin1_mblen, pg_latin1_dsplen, pg_latin1_verifychar, pg_latin1_verifystr, 1},
1830 : : [PG_WIN1258] = {pg_latin12wchar_with_len, pg_wchar2single_with_len, pg_latin1_mblen, pg_latin1_dsplen, pg_latin1_verifychar, pg_latin1_verifystr, 1},
1831 : : [PG_WIN866] = {pg_latin12wchar_with_len, pg_wchar2single_with_len, pg_latin1_mblen, pg_latin1_dsplen, pg_latin1_verifychar, pg_latin1_verifystr, 1},
1832 : : [PG_WIN874] = {pg_latin12wchar_with_len, pg_wchar2single_with_len, pg_latin1_mblen, pg_latin1_dsplen, pg_latin1_verifychar, pg_latin1_verifystr, 1},
1833 : : [PG_KOI8R] = {pg_latin12wchar_with_len, pg_wchar2single_with_len, pg_latin1_mblen, pg_latin1_dsplen, pg_latin1_verifychar, pg_latin1_verifystr, 1},
1834 : : [PG_WIN1251] = {pg_latin12wchar_with_len, pg_wchar2single_with_len, pg_latin1_mblen, pg_latin1_dsplen, pg_latin1_verifychar, pg_latin1_verifystr, 1},
1835 : : [PG_WIN1252] = {pg_latin12wchar_with_len, pg_wchar2single_with_len, pg_latin1_mblen, pg_latin1_dsplen, pg_latin1_verifychar, pg_latin1_verifystr, 1},
1836 : : [PG_ISO_8859_5] = {pg_latin12wchar_with_len, pg_wchar2single_with_len, pg_latin1_mblen, pg_latin1_dsplen, pg_latin1_verifychar, pg_latin1_verifystr, 1},
1837 : : [PG_ISO_8859_6] = {pg_latin12wchar_with_len, pg_wchar2single_with_len, pg_latin1_mblen, pg_latin1_dsplen, pg_latin1_verifychar, pg_latin1_verifystr, 1},
1838 : : [PG_ISO_8859_7] = {pg_latin12wchar_with_len, pg_wchar2single_with_len, pg_latin1_mblen, pg_latin1_dsplen, pg_latin1_verifychar, pg_latin1_verifystr, 1},
1839 : : [PG_ISO_8859_8] = {pg_latin12wchar_with_len, pg_wchar2single_with_len, pg_latin1_mblen, pg_latin1_dsplen, pg_latin1_verifychar, pg_latin1_verifystr, 1},
1840 : : [PG_WIN1250] = {pg_latin12wchar_with_len, pg_wchar2single_with_len, pg_latin1_mblen, pg_latin1_dsplen, pg_latin1_verifychar, pg_latin1_verifystr, 1},
1841 : : [PG_WIN1253] = {pg_latin12wchar_with_len, pg_wchar2single_with_len, pg_latin1_mblen, pg_latin1_dsplen, pg_latin1_verifychar, pg_latin1_verifystr, 1},
1842 : : [PG_WIN1254] = {pg_latin12wchar_with_len, pg_wchar2single_with_len, pg_latin1_mblen, pg_latin1_dsplen, pg_latin1_verifychar, pg_latin1_verifystr, 1},
1843 : : [PG_WIN1255] = {pg_latin12wchar_with_len, pg_wchar2single_with_len, pg_latin1_mblen, pg_latin1_dsplen, pg_latin1_verifychar, pg_latin1_verifystr, 1},
1844 : : [PG_WIN1257] = {pg_latin12wchar_with_len, pg_wchar2single_with_len, pg_latin1_mblen, pg_latin1_dsplen, pg_latin1_verifychar, pg_latin1_verifystr, 1},
1845 : : [PG_KOI8U] = {pg_latin12wchar_with_len, pg_wchar2single_with_len, pg_latin1_mblen, pg_latin1_dsplen, pg_latin1_verifychar, pg_latin1_verifystr, 1},
1846 : : [PG_SJIS] = {0, 0, pg_sjis_mblen, pg_sjis_dsplen, pg_sjis_verifychar, pg_sjis_verifystr, 2},
1847 : : [PG_BIG5] = {0, 0, pg_big5_mblen, pg_big5_dsplen, pg_big5_verifychar, pg_big5_verifystr, 2},
1848 : : [PG_GBK] = {0, 0, pg_gbk_mblen, pg_gbk_dsplen, pg_gbk_verifychar, pg_gbk_verifystr, 2},
1849 : : [PG_UHC] = {0, 0, pg_uhc_mblen, pg_uhc_dsplen, pg_uhc_verifychar, pg_uhc_verifystr, 2},
1850 : : [PG_GB18030] = {0, 0, pg_gb18030_mblen, pg_gb18030_dsplen, pg_gb18030_verifychar, pg_gb18030_verifystr, 4},
1851 : : [PG_JOHAB] = {0, 0, pg_johab_mblen, pg_johab_dsplen, pg_johab_verifychar, pg_johab_verifystr, 3},
1852 : : [PG_SHIFT_JIS_2004] = {0, 0, pg_sjis_mblen, pg_sjis_dsplen, pg_sjis_verifychar, pg_sjis_verifystr, 2},
1853 : : };
1854 : :
1855 : : /*
1856 : : * Returns the byte length of a multibyte character.
1857 : : *
1858 : : * Choose "mblen" functions based on the input string characteristics.
1859 : : * pg_encoding_mblen() can be used when ANY of these conditions are met:
1860 : : *
1861 : : * - The input string is zero-terminated
1862 : : *
1863 : : * - The input string is known to be valid in the encoding (e.g., string
1864 : : * converted from database encoding)
1865 : : *
1866 : : * - The encoding is not GB18030 (e.g., when only database encodings are
1867 : : * passed to 'encoding' parameter)
1868 : : *
1869 : : * encoding==GB18030 requires examining up to two bytes to determine character
1870 : : * length. Therefore, callers satisfying none of those conditions must use
1871 : : * pg_encoding_mblen_or_incomplete() instead, as access to mbstr[1] cannot be
1872 : : * guaranteed to be within allocation bounds.
1873 : : *
1874 : : * When dealing with text that is not certainly valid in the specified
1875 : : * encoding, the result may exceed the actual remaining string length.
1876 : : * Callers that are not prepared to deal with that should use Min(remaining,
1877 : : * pg_encoding_mblen_or_incomplete()). For zero-terminated strings, that and
1878 : : * pg_encoding_mblen_bounded() are interchangeable.
1879 : : */
1880 : : int
7473 tgl@sss.pgh.pa.us 1881 : 30496674 : pg_encoding_mblen(int encoding, const char *mbstr)
1882 : : {
4609 bruce@momjian.us 1883 [ + - + - ]: 30496674 : return (PG_VALID_ENCODING(encoding) ?
3346 peter_e@gmx.net 1884 [ + - ]: 60993348 : pg_wchar_table[encoding].mblen((const unsigned char *) mbstr) :
3346 peter_e@gmx.net 1885 :UBC 0 : pg_wchar_table[PG_SQL_ASCII].mblen((const unsigned char *) mbstr));
1886 : : }
1887 : :
1888 : : /*
1889 : : * Returns the byte length of a multibyte character (possibly not
1890 : : * zero-terminated), or INT_MAX if too few bytes remain to determine a length.
1891 : : */
1892 : : int
549 noah@leadboat.com 1893 :CBC 3136 : pg_encoding_mblen_or_incomplete(int encoding, const char *mbstr,
1894 : : size_t remaining)
1895 : : {
1896 : : /*
1897 : : * Define zero remaining as too few, even for single-byte encodings.
1898 : : * pg_gb18030_mblen() reads one or two bytes; single-byte encodings read
1899 : : * zero; others read one.
1900 : : */
1901 [ + - + + ]: 3136 : if (remaining < 1 ||
1902 [ + + + + ]: 202 : (encoding == PG_GB18030 && IS_HIGHBIT_SET(*mbstr) && remaining < 2))
1903 : 42 : return INT_MAX;
1904 : 3094 : return pg_encoding_mblen(encoding, mbstr);
1905 : : }
1906 : :
1907 : : /*
1908 : : * Returns the byte length of a multibyte character; but not more than the
1909 : : * distance to the terminating zero byte. For input that might lack a
1910 : : * terminating zero, use Min(remaining, pg_encoding_mblen_or_incomplete()).
1911 : : */
1912 : : int
1977 tgl@sss.pgh.pa.us 1913 :UBC 0 : pg_encoding_mblen_bounded(int encoding, const char *mbstr)
1914 : : {
1915 : 0 : return strnlen(mbstr, pg_encoding_mblen(encoding, mbstr));
1916 : : }
1917 : :
1918 : : /*
1919 : : * Returns the display length of a multibyte character.
1920 : : */
1921 : : int
7473 tgl@sss.pgh.pa.us 1922 :CBC 30383731 : pg_encoding_dsplen(int encoding, const char *mbstr)
1923 : : {
4609 bruce@momjian.us 1924 [ + - + - ]: 30383731 : return (PG_VALID_ENCODING(encoding) ?
3346 peter_e@gmx.net 1925 [ + - ]: 60767462 : pg_wchar_table[encoding].dsplen((const unsigned char *) mbstr) :
3346 peter_e@gmx.net 1926 :UBC 0 : pg_wchar_table[PG_SQL_ASCII].dsplen((const unsigned char *) mbstr));
1927 : : }
1928 : :
1929 : : /*
1930 : : * Verify the first multibyte character of the given string.
1931 : : *
1932 : : * Return its byte length if good, -1 if bad. (See comments at pg_wchar_tbl
1933 : : * for full details of the mbverifychar API.)
1934 : : */
1935 : : int
2107 heikki.linnakangas@i 1936 :CBC 4228 : pg_encoding_verifymbchar(int encoding, const char *mbstr, int len)
1937 : : {
1938 [ + - + - ]: 4228 : return (PG_VALID_ENCODING(encoding) ?
1939 [ + - ]: 8456 : pg_wchar_table[encoding].mbverifychar((const unsigned char *) mbstr, len) :
2107 heikki.linnakangas@i 1940 :UBC 0 : pg_wchar_table[PG_SQL_ASCII].mbverifychar((const unsigned char *) mbstr, len));
1941 : : }
1942 : :
1943 : : /*
1944 : : * Verify that a string is valid for the given encoding.
1945 : : *
1946 : : * Returns the number of input bytes (<= len) that form a valid string. (See
1947 : : * comments at pg_wchar_tbl for full details of the mbverifystr API.)
1948 : : */
1949 : : int
2107 heikki.linnakangas@i 1950 :CBC 267494 : pg_encoding_verifymbstr(int encoding, const char *mbstr, int len)
1951 : : {
4609 bruce@momjian.us 1952 [ + - + - ]: 267494 : return (PG_VALID_ENCODING(encoding) ?
2107 heikki.linnakangas@i 1953 [ + - ]: 534988 : pg_wchar_table[encoding].mbverifystr((const unsigned char *) mbstr, len) :
2107 heikki.linnakangas@i 1954 :UBC 0 : pg_wchar_table[PG_SQL_ASCII].mbverifystr((const unsigned char *) mbstr, len));
1955 : : }
1956 : :
1957 : : /*
1958 : : * fetch maximum length of a given encoding
1959 : : */
1960 : : int
7473 tgl@sss.pgh.pa.us 1961 :CBC 736125 : pg_encoding_max_length(int encoding)
1962 : : {
1963 [ + - + - : 736125 : Assert(PG_VALID_ENCODING(encoding));
+ + ]
1964 : :
1965 : : /*
1966 : : * Check for the encoding despite the assert, due to some mingw versions
1967 : : * otherwise issuing bogus warnings.
1968 : : */
633 andres@anarazel.de 1969 [ + - + - ]: 736125 : return PG_VALID_ENCODING(encoding) ?
1970 [ + - ]: 1472250 : pg_wchar_table[encoding].maxmblen :
1971 : : pg_wchar_table[PG_SQL_ASCII].maxmblen;
1972 : : }
|