Line data Source code
1 : /*-------------------------------------------------------------------------
2 : *
3 : * encode.c
4 : * Various data encoding/decoding things.
5 : *
6 : * Copyright (c) 2001-2025, PostgreSQL Global Development Group
7 : *
8 : *
9 : * IDENTIFICATION
10 : * src/backend/utils/adt/encode.c
11 : *
12 : *-------------------------------------------------------------------------
13 : */
14 : #include "postgres.h"
15 :
16 : #include <ctype.h>
17 :
18 : #include "mb/pg_wchar.h"
19 : #include "utils/builtins.h"
20 : #include "utils/memutils.h"
21 : #include "varatt.h"
22 :
23 :
24 : /*
25 : * Encoding conversion API.
26 : * encode_len() and decode_len() compute the amount of space needed, while
27 : * encode() and decode() perform the actual conversions. It is okay for
28 : * the _len functions to return an overestimate, but not an underestimate.
29 : * (Having said that, large overestimates could cause unnecessary errors,
30 : * so it's better to get it right.) The conversion routines write to the
31 : * buffer at *res and return the true length of their output.
32 : */
33 : struct pg_encoding
34 : {
35 : uint64 (*encode_len) (const char *data, size_t dlen);
36 : uint64 (*decode_len) (const char *data, size_t dlen);
37 : uint64 (*encode) (const char *data, size_t dlen, char *res);
38 : uint64 (*decode) (const char *data, size_t dlen, char *res);
39 : };
40 :
41 : static const struct pg_encoding *pg_find_encoding(const char *name);
42 :
43 : /*
44 : * SQL functions.
45 : */
46 :
47 : Datum
48 485416 : binary_encode(PG_FUNCTION_ARGS)
49 : {
50 485416 : bytea *data = PG_GETARG_BYTEA_PP(0);
51 485416 : Datum name = PG_GETARG_DATUM(1);
52 : text *result;
53 : char *namebuf;
54 : char *dataptr;
55 : size_t datalen;
56 : uint64 resultlen;
57 : uint64 res;
58 : const struct pg_encoding *enc;
59 :
60 485416 : namebuf = TextDatumGetCString(name);
61 :
62 485416 : enc = pg_find_encoding(namebuf);
63 485416 : if (enc == NULL)
64 0 : ereport(ERROR,
65 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
66 : errmsg("unrecognized encoding: \"%s\"", namebuf)));
67 :
68 485416 : dataptr = VARDATA_ANY(data);
69 485416 : datalen = VARSIZE_ANY_EXHDR(data);
70 :
71 485416 : resultlen = enc->encode_len(dataptr, datalen);
72 :
73 : /*
74 : * resultlen possibly overflows uint32, therefore on 32-bit machines it's
75 : * unsafe to rely on palloc's internal check.
76 : */
77 485416 : if (resultlen > MaxAllocSize - VARHDRSZ)
78 0 : ereport(ERROR,
79 : (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
80 : errmsg("result of encoding conversion is too large")));
81 :
82 485416 : result = palloc(VARHDRSZ + resultlen);
83 :
84 485416 : res = enc->encode(dataptr, datalen, VARDATA(result));
85 :
86 : /* Make this FATAL 'cause we've trodden on memory ... */
87 485416 : if (res > resultlen)
88 0 : elog(FATAL, "overflow - encode estimate too small");
89 :
90 485416 : SET_VARSIZE(result, VARHDRSZ + res);
91 :
92 485416 : PG_RETURN_TEXT_P(result);
93 : }
94 :
95 : Datum
96 32864 : binary_decode(PG_FUNCTION_ARGS)
97 : {
98 32864 : text *data = PG_GETARG_TEXT_PP(0);
99 32864 : Datum name = PG_GETARG_DATUM(1);
100 : bytea *result;
101 : char *namebuf;
102 : char *dataptr;
103 : size_t datalen;
104 : uint64 resultlen;
105 : uint64 res;
106 : const struct pg_encoding *enc;
107 :
108 32864 : namebuf = TextDatumGetCString(name);
109 :
110 32864 : enc = pg_find_encoding(namebuf);
111 32864 : if (enc == NULL)
112 0 : ereport(ERROR,
113 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
114 : errmsg("unrecognized encoding: \"%s\"", namebuf)));
115 :
116 32864 : dataptr = VARDATA_ANY(data);
117 32864 : datalen = VARSIZE_ANY_EXHDR(data);
118 :
119 32864 : resultlen = enc->decode_len(dataptr, datalen);
120 :
121 : /*
122 : * resultlen possibly overflows uint32, therefore on 32-bit machines it's
123 : * unsafe to rely on palloc's internal check.
124 : */
125 32864 : if (resultlen > MaxAllocSize - VARHDRSZ)
126 0 : ereport(ERROR,
127 : (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
128 : errmsg("result of decoding conversion is too large")));
129 :
130 32864 : result = palloc(VARHDRSZ + resultlen);
131 :
132 32864 : res = enc->decode(dataptr, datalen, VARDATA(result));
133 :
134 : /* Make this FATAL 'cause we've trodden on memory ... */
135 32864 : if (res > resultlen)
136 0 : elog(FATAL, "overflow - decode estimate too small");
137 :
138 32864 : SET_VARSIZE(result, VARHDRSZ + res);
139 :
140 32864 : PG_RETURN_BYTEA_P(result);
141 : }
142 :
143 :
144 : /*
145 : * HEX
146 : */
147 :
148 : /*
149 : * The hex expansion of each possible byte value (two chars per value).
150 : */
151 : static const char hextbl[512] =
152 : "000102030405060708090a0b0c0d0e0f"
153 : "101112131415161718191a1b1c1d1e1f"
154 : "202122232425262728292a2b2c2d2e2f"
155 : "303132333435363738393a3b3c3d3e3f"
156 : "404142434445464748494a4b4c4d4e4f"
157 : "505152535455565758595a5b5c5d5e5f"
158 : "606162636465666768696a6b6c6d6e6f"
159 : "707172737475767778797a7b7c7d7e7f"
160 : "808182838485868788898a8b8c8d8e8f"
161 : "909192939495969798999a9b9c9d9e9f"
162 : "a0a1a2a3a4a5a6a7a8a9aaabacadaeaf"
163 : "b0b1b2b3b4b5b6b7b8b9babbbcbdbebf"
164 : "c0c1c2c3c4c5c6c7c8c9cacbcccdcecf"
165 : "d0d1d2d3d4d5d6d7d8d9dadbdcdddedf"
166 : "e0e1e2e3e4e5e6e7e8e9eaebecedeeef"
167 : "f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff";
168 :
169 : static const int8 hexlookup[128] = {
170 : -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
171 : -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
172 : -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
173 : 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1,
174 : -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1,
175 : -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
176 : -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1,
177 : -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
178 : };
179 :
180 : uint64
181 945138 : hex_encode(const char *src, size_t len, char *dst)
182 : {
183 945138 : const char *end = src + len;
184 :
185 23188806 : while (src < end)
186 : {
187 22243668 : unsigned char usrc = *((const unsigned char *) src);
188 :
189 22243668 : memcpy(dst, &hextbl[2 * usrc], 2);
190 22243668 : src++;
191 22243668 : dst += 2;
192 : }
193 945138 : return (uint64) len * 2;
194 : }
195 :
196 : static inline bool
197 7924530 : get_hex(const char *cp, char *out)
198 : {
199 7924530 : unsigned char c = (unsigned char) *cp;
200 7924530 : int res = -1;
201 :
202 7924530 : if (c < 127)
203 7924530 : res = hexlookup[c];
204 :
205 7924530 : *out = (char) res;
206 :
207 7924530 : return (res >= 0);
208 : }
209 :
210 : uint64
211 32824 : hex_decode(const char *src, size_t len, char *dst)
212 : {
213 32824 : return hex_decode_safe(src, len, dst, NULL);
214 : }
215 :
216 : uint64
217 143956 : hex_decode_safe(const char *src, size_t len, char *dst, Node *escontext)
218 : {
219 : const char *s,
220 : *srcend;
221 : char v1,
222 : v2,
223 : *p;
224 :
225 143956 : srcend = src + len;
226 143956 : s = src;
227 143956 : p = dst;
228 4106260 : while (s < srcend)
229 : {
230 3962334 : if (*s == ' ' || *s == '\n' || *s == '\t' || *s == '\r')
231 : {
232 60 : s++;
233 60 : continue;
234 : }
235 3962274 : if (!get_hex(s, &v1))
236 0 : ereturn(escontext, 0,
237 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
238 : errmsg("invalid hexadecimal digit: \"%.*s\"",
239 : pg_mblen(s), s)));
240 3962274 : s++;
241 3962274 : if (s >= srcend)
242 18 : ereturn(escontext, 0,
243 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
244 : errmsg("invalid hexadecimal data: odd number of digits")));
245 3962256 : if (!get_hex(s, &v2))
246 12 : ereturn(escontext, 0,
247 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
248 : errmsg("invalid hexadecimal digit: \"%.*s\"",
249 : pg_mblen(s), s)));
250 3962244 : s++;
251 3962244 : *p++ = (v1 << 4) | v2;
252 : }
253 :
254 143926 : return p - dst;
255 : }
256 :
257 : static uint64
258 485328 : hex_enc_len(const char *src, size_t srclen)
259 : {
260 485328 : return (uint64) srclen << 1;
261 : }
262 :
263 : static uint64
264 32824 : hex_dec_len(const char *src, size_t srclen)
265 : {
266 32824 : return (uint64) srclen >> 1;
267 : }
268 :
269 : /*
270 : * BASE64
271 : */
272 :
273 : static const char _base64[] =
274 : "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
275 :
276 : static const int8 b64lookup[128] = {
277 : -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
278 : -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
279 : -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63,
280 : 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1,
281 : -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
282 : 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1,
283 : -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
284 : 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1,
285 : };
286 :
287 : static uint64
288 12 : pg_base64_encode(const char *src, size_t len, char *dst)
289 : {
290 : char *p,
291 12 : *lend = dst + 76;
292 : const char *s,
293 12 : *end = src + len;
294 12 : int pos = 2;
295 12 : uint32 buf = 0;
296 :
297 12 : s = src;
298 12 : p = dst;
299 :
300 852 : while (s < end)
301 : {
302 840 : buf |= (unsigned char) *s << (pos << 3);
303 840 : pos--;
304 840 : s++;
305 :
306 : /* write it out */
307 840 : if (pos < 0)
308 : {
309 276 : *p++ = _base64[(buf >> 18) & 0x3f];
310 276 : *p++ = _base64[(buf >> 12) & 0x3f];
311 276 : *p++ = _base64[(buf >> 6) & 0x3f];
312 276 : *p++ = _base64[buf & 0x3f];
313 :
314 276 : pos = 2;
315 276 : buf = 0;
316 : }
317 840 : if (p >= lend)
318 : {
319 12 : *p++ = '\n';
320 12 : lend = p + 76;
321 : }
322 : }
323 12 : if (pos != 2)
324 : {
325 12 : *p++ = _base64[(buf >> 18) & 0x3f];
326 12 : *p++ = _base64[(buf >> 12) & 0x3f];
327 12 : *p++ = (pos == 0) ? _base64[(buf >> 6) & 0x3f] : '=';
328 12 : *p++ = '=';
329 : }
330 :
331 12 : return p - dst;
332 : }
333 :
334 : static uint64
335 10 : pg_base64_decode(const char *src, size_t len, char *dst)
336 : {
337 10 : const char *srcend = src + len,
338 10 : *s = src;
339 10 : char *p = dst;
340 : char c;
341 10 : int b = 0;
342 10 : uint32 buf = 0;
343 10 : int pos = 0,
344 10 : end = 0;
345 :
346 624 : while (s < srcend)
347 : {
348 614 : c = *s++;
349 :
350 614 : if (c == ' ' || c == '\t' || c == '\n' || c == '\r')
351 6 : continue;
352 :
353 608 : if (c == '=')
354 : {
355 : /* end sequence */
356 16 : if (!end)
357 : {
358 10 : if (pos == 2)
359 6 : end = 1;
360 4 : else if (pos == 3)
361 4 : end = 2;
362 : else
363 0 : ereport(ERROR,
364 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
365 : errmsg("unexpected \"=\" while decoding base64 sequence")));
366 : }
367 16 : b = 0;
368 : }
369 : else
370 : {
371 592 : b = -1;
372 592 : if (c > 0 && c < 127)
373 592 : b = b64lookup[(unsigned char) c];
374 592 : if (b < 0)
375 0 : ereport(ERROR,
376 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
377 : errmsg("invalid symbol \"%.*s\" found while decoding base64 sequence",
378 : pg_mblen(s - 1), s - 1)));
379 : }
380 : /* add it to buffer */
381 608 : buf = (buf << 6) + b;
382 608 : pos++;
383 608 : if (pos == 4)
384 : {
385 152 : *p++ = (buf >> 16) & 255;
386 152 : if (end == 0 || end > 1)
387 146 : *p++ = (buf >> 8) & 255;
388 152 : if (end == 0 || end > 2)
389 142 : *p++ = buf & 255;
390 152 : buf = 0;
391 152 : pos = 0;
392 : }
393 : }
394 :
395 10 : if (pos != 0)
396 0 : ereport(ERROR,
397 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
398 : errmsg("invalid base64 end sequence"),
399 : errhint("Input data is missing padding, is truncated, or is otherwise corrupted.")));
400 :
401 10 : return p - dst;
402 : }
403 :
404 :
405 : static uint64
406 12 : pg_base64_enc_len(const char *src, size_t srclen)
407 : {
408 : /* 3 bytes will be converted to 4, linefeed after 76 chars */
409 12 : return ((uint64) srclen + 2) / 3 * 4 + (uint64) srclen / (76 * 3 / 4);
410 : }
411 :
412 : static uint64
413 10 : pg_base64_dec_len(const char *src, size_t srclen)
414 : {
415 10 : return ((uint64) srclen * 3) >> 2;
416 : }
417 :
418 : /*
419 : * Escape
420 : * Minimally escape bytea to text.
421 : * De-escape text to bytea.
422 : *
423 : * We must escape zero bytes and high-bit-set bytes to avoid generating
424 : * text that might be invalid in the current encoding, or that might
425 : * change to something else if passed through an encoding conversion
426 : * (leading to failing to de-escape to the original bytea value).
427 : * Also of course backslash itself has to be escaped.
428 : *
429 : * De-escaping processes \\ and any \### octal
430 : */
431 :
432 : #define VAL(CH) ((CH) - '0')
433 : #define DIG(VAL) ((VAL) + '0')
434 :
435 : static uint64
436 76 : esc_encode(const char *src, size_t srclen, char *dst)
437 : {
438 76 : const char *end = src + srclen;
439 76 : char *rp = dst;
440 76 : uint64 len = 0;
441 :
442 732 : while (src < end)
443 : {
444 656 : unsigned char c = (unsigned char) *src;
445 :
446 656 : if (c == '\0' || IS_HIGHBIT_SET(c))
447 : {
448 82 : rp[0] = '\\';
449 82 : rp[1] = DIG(c >> 6);
450 82 : rp[2] = DIG((c >> 3) & 7);
451 82 : rp[3] = DIG(c & 7);
452 82 : rp += 4;
453 82 : len += 4;
454 : }
455 574 : else if (c == '\\')
456 : {
457 0 : rp[0] = '\\';
458 0 : rp[1] = '\\';
459 0 : rp += 2;
460 0 : len += 2;
461 : }
462 : else
463 : {
464 574 : *rp++ = c;
465 574 : len++;
466 : }
467 :
468 656 : src++;
469 : }
470 :
471 76 : return len;
472 : }
473 :
474 : static uint64
475 30 : esc_decode(const char *src, size_t srclen, char *dst)
476 : {
477 30 : const char *end = src + srclen;
478 30 : char *rp = dst;
479 30 : uint64 len = 0;
480 :
481 2400084 : while (src < end)
482 : {
483 2400054 : if (src[0] != '\\')
484 2400024 : *rp++ = *src++;
485 30 : else if (src + 3 < end &&
486 30 : (src[1] >= '0' && src[1] <= '3') &&
487 30 : (src[2] >= '0' && src[2] <= '7') &&
488 30 : (src[3] >= '0' && src[3] <= '7'))
489 30 : {
490 : int val;
491 :
492 30 : val = VAL(src[1]);
493 30 : val <<= 3;
494 30 : val += VAL(src[2]);
495 30 : val <<= 3;
496 30 : *rp++ = val + VAL(src[3]);
497 30 : src += 4;
498 : }
499 0 : else if (src + 1 < end &&
500 0 : (src[1] == '\\'))
501 : {
502 0 : *rp++ = '\\';
503 0 : src += 2;
504 : }
505 : else
506 : {
507 : /*
508 : * One backslash, not followed by ### valid octal. Should never
509 : * get here, since esc_dec_len does same check.
510 : */
511 0 : ereport(ERROR,
512 : (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
513 : errmsg("invalid input syntax for type %s", "bytea")));
514 : }
515 :
516 2400054 : len++;
517 : }
518 :
519 30 : return len;
520 : }
521 :
522 : static uint64
523 76 : esc_enc_len(const char *src, size_t srclen)
524 : {
525 76 : const char *end = src + srclen;
526 76 : uint64 len = 0;
527 :
528 732 : while (src < end)
529 : {
530 656 : if (*src == '\0' || IS_HIGHBIT_SET(*src))
531 82 : len += 4;
532 574 : else if (*src == '\\')
533 0 : len += 2;
534 : else
535 574 : len++;
536 :
537 656 : src++;
538 : }
539 :
540 76 : return len;
541 : }
542 :
543 : static uint64
544 30 : esc_dec_len(const char *src, size_t srclen)
545 : {
546 30 : const char *end = src + srclen;
547 30 : uint64 len = 0;
548 :
549 2400084 : while (src < end)
550 : {
551 2400054 : if (src[0] != '\\')
552 2400024 : src++;
553 30 : else if (src + 3 < end &&
554 30 : (src[1] >= '0' && src[1] <= '3') &&
555 30 : (src[2] >= '0' && src[2] <= '7') &&
556 30 : (src[3] >= '0' && src[3] <= '7'))
557 : {
558 : /*
559 : * backslash + valid octal
560 : */
561 30 : src += 4;
562 : }
563 0 : else if (src + 1 < end &&
564 0 : (src[1] == '\\'))
565 : {
566 : /*
567 : * two backslashes = backslash
568 : */
569 0 : src += 2;
570 : }
571 : else
572 : {
573 : /*
574 : * one backslash, not followed by ### valid octal
575 : */
576 0 : ereport(ERROR,
577 : (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
578 : errmsg("invalid input syntax for type %s", "bytea")));
579 : }
580 :
581 2400054 : len++;
582 : }
583 30 : return len;
584 : }
585 :
586 : /*
587 : * Common
588 : */
589 :
590 : static const struct
591 : {
592 : const char *name;
593 : struct pg_encoding enc;
594 : } enclist[] =
595 :
596 : {
597 : {
598 : "hex",
599 : {
600 : hex_enc_len, hex_dec_len, hex_encode, hex_decode
601 : }
602 : },
603 : {
604 : "base64",
605 : {
606 : pg_base64_enc_len, pg_base64_dec_len, pg_base64_encode, pg_base64_decode
607 : }
608 : },
609 : {
610 : "escape",
611 : {
612 : esc_enc_len, esc_dec_len, esc_encode, esc_decode
613 : }
614 : },
615 : {
616 : NULL,
617 : {
618 : NULL, NULL, NULL, NULL
619 : }
620 : }
621 : };
622 :
623 : static const struct pg_encoding *
624 518280 : pg_find_encoding(const char *name)
625 : {
626 : int i;
627 :
628 518514 : for (i = 0; enclist[i].name; i++)
629 518514 : if (pg_strcasecmp(enclist[i].name, name) == 0)
630 518280 : return &enclist[i].enc;
631 :
632 0 : return NULL;
633 : }
|