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 "port/simd.h"
20 : #include "utils/builtins.h"
21 : #include "utils/memutils.h"
22 : #include "varatt.h"
23 :
24 :
25 : /*
26 : * Encoding conversion API.
27 : * encode_len() and decode_len() compute the amount of space needed, while
28 : * encode() and decode() perform the actual conversions. It is okay for
29 : * the _len functions to return an overestimate, but not an underestimate.
30 : * (Having said that, large overestimates could cause unnecessary errors,
31 : * so it's better to get it right.) The conversion routines write to the
32 : * buffer at *res and return the true length of their output.
33 : */
34 : struct pg_encoding
35 : {
36 : uint64 (*encode_len) (const char *data, size_t dlen);
37 : uint64 (*decode_len) (const char *data, size_t dlen);
38 : uint64 (*encode) (const char *data, size_t dlen, char *res);
39 : uint64 (*decode) (const char *data, size_t dlen, char *res);
40 : };
41 :
42 : static const struct pg_encoding *pg_find_encoding(const char *name);
43 :
44 : /*
45 : * SQL functions.
46 : */
47 :
48 : Datum
49 526478 : binary_encode(PG_FUNCTION_ARGS)
50 : {
51 526478 : bytea *data = PG_GETARG_BYTEA_PP(0);
52 526478 : Datum name = PG_GETARG_DATUM(1);
53 : text *result;
54 : char *namebuf;
55 : char *dataptr;
56 : size_t datalen;
57 : uint64 resultlen;
58 : uint64 res;
59 : const struct pg_encoding *enc;
60 :
61 526478 : namebuf = TextDatumGetCString(name);
62 :
63 526478 : enc = pg_find_encoding(namebuf);
64 526478 : if (enc == NULL)
65 6 : ereport(ERROR,
66 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
67 : errmsg("unrecognized encoding: \"%s\"", namebuf),
68 : errhint("Valid encodings are \"%s\", \"%s\", \"%s\", and \"%s\".",
69 : "base64", "base64url", "escape", "hex")));
70 :
71 526472 : dataptr = VARDATA_ANY(data);
72 526472 : datalen = VARSIZE_ANY_EXHDR(data);
73 :
74 526472 : resultlen = enc->encode_len(dataptr, datalen);
75 :
76 : /*
77 : * resultlen possibly overflows uint32, therefore on 32-bit machines it's
78 : * unsafe to rely on palloc's internal check.
79 : */
80 526472 : if (resultlen > MaxAllocSize - VARHDRSZ)
81 0 : ereport(ERROR,
82 : (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
83 : errmsg("result of encoding conversion is too large")));
84 :
85 526472 : result = palloc(VARHDRSZ + resultlen);
86 :
87 526472 : res = enc->encode(dataptr, datalen, VARDATA(result));
88 :
89 : /* Make this FATAL 'cause we've trodden on memory ... */
90 526472 : if (res > resultlen)
91 0 : elog(FATAL, "overflow - encode estimate too small");
92 :
93 526472 : SET_VARSIZE(result, VARHDRSZ + res);
94 :
95 526472 : PG_RETURN_TEXT_P(result);
96 : }
97 :
98 : Datum
99 32978 : binary_decode(PG_FUNCTION_ARGS)
100 : {
101 32978 : text *data = PG_GETARG_TEXT_PP(0);
102 32978 : Datum name = PG_GETARG_DATUM(1);
103 : bytea *result;
104 : char *namebuf;
105 : char *dataptr;
106 : size_t datalen;
107 : uint64 resultlen;
108 : uint64 res;
109 : const struct pg_encoding *enc;
110 :
111 32978 : namebuf = TextDatumGetCString(name);
112 :
113 32978 : enc = pg_find_encoding(namebuf);
114 32978 : if (enc == NULL)
115 6 : ereport(ERROR,
116 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
117 : errmsg("unrecognized encoding: \"%s\"", namebuf),
118 : errhint("Valid encodings are \"%s\", \"%s\", \"%s\", and \"%s\".",
119 : "base64", "base64url", "escape", "hex")));
120 :
121 32972 : dataptr = VARDATA_ANY(data);
122 32972 : datalen = VARSIZE_ANY_EXHDR(data);
123 :
124 32972 : resultlen = enc->decode_len(dataptr, datalen);
125 :
126 : /*
127 : * resultlen possibly overflows uint32, therefore on 32-bit machines it's
128 : * unsafe to rely on palloc's internal check.
129 : */
130 32972 : if (resultlen > MaxAllocSize - VARHDRSZ)
131 0 : ereport(ERROR,
132 : (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
133 : errmsg("result of decoding conversion is too large")));
134 :
135 32972 : result = palloc(VARHDRSZ + resultlen);
136 :
137 32972 : res = enc->decode(dataptr, datalen, VARDATA(result));
138 :
139 : /* Make this FATAL 'cause we've trodden on memory ... */
140 32954 : if (res > resultlen)
141 0 : elog(FATAL, "overflow - decode estimate too small");
142 :
143 32954 : SET_VARSIZE(result, VARHDRSZ + res);
144 :
145 32954 : PG_RETURN_BYTEA_P(result);
146 : }
147 :
148 :
149 : /*
150 : * HEX
151 : */
152 :
153 : /*
154 : * The hex expansion of each possible byte value (two chars per value).
155 : */
156 : static const char hextbl[512] =
157 : "000102030405060708090a0b0c0d0e0f"
158 : "101112131415161718191a1b1c1d1e1f"
159 : "202122232425262728292a2b2c2d2e2f"
160 : "303132333435363738393a3b3c3d3e3f"
161 : "404142434445464748494a4b4c4d4e4f"
162 : "505152535455565758595a5b5c5d5e5f"
163 : "606162636465666768696a6b6c6d6e6f"
164 : "707172737475767778797a7b7c7d7e7f"
165 : "808182838485868788898a8b8c8d8e8f"
166 : "909192939495969798999a9b9c9d9e9f"
167 : "a0a1a2a3a4a5a6a7a8a9aaabacadaeaf"
168 : "b0b1b2b3b4b5b6b7b8b9babbbcbdbebf"
169 : "c0c1c2c3c4c5c6c7c8c9cacbcccdcecf"
170 : "d0d1d2d3d4d5d6d7d8d9dadbdcdddedf"
171 : "e0e1e2e3e4e5e6e7e8e9eaebecedeeef"
172 : "f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff";
173 :
174 : static const int8 hexlookup[128] = {
175 : -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
176 : -1, -1, -1, -1, -1, -1, -1, -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 : 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1,
179 : -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1,
180 : -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
181 : -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1,
182 : -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
183 : };
184 :
185 : static inline uint64
186 1399240 : hex_encode_scalar(const char *src, size_t len, char *dst)
187 : {
188 1399240 : const char *end = src + len;
189 :
190 2706852 : while (src < end)
191 : {
192 1307612 : unsigned char usrc = *((const unsigned char *) src);
193 :
194 1307612 : memcpy(dst, &hextbl[2 * usrc], 2);
195 1307612 : src++;
196 1307612 : dst += 2;
197 : }
198 1399240 : return (uint64) len * 2;
199 : }
200 :
201 : uint64
202 1399240 : hex_encode(const char *src, size_t len, char *dst)
203 : {
204 : #ifdef USE_NO_SIMD
205 : return hex_encode_scalar(src, len, dst);
206 : #else
207 1399240 : const uint64 tail_idx = len & ~(sizeof(Vector8) - 1);
208 : uint64 i;
209 :
210 : /*
211 : * This splits the high and low nibbles of each byte into separate
212 : * vectors, adds the vectors to a mask that converts the nibbles to their
213 : * equivalent ASCII bytes, and interleaves those bytes back together to
214 : * form the final hex-encoded string.
215 : */
216 3593648 : for (i = 0; i < tail_idx; i += sizeof(Vector8))
217 : {
218 : Vector8 srcv;
219 : Vector8 lo;
220 : Vector8 hi;
221 : Vector8 mask;
222 :
223 2194408 : vector8_load(&srcv, (const uint8 *) &src[i]);
224 :
225 2194408 : lo = vector8_and(srcv, vector8_broadcast(0x0f));
226 2194408 : mask = vector8_gt(lo, vector8_broadcast(0x9));
227 2194408 : mask = vector8_and(mask, vector8_broadcast('a' - '0' - 10));
228 2194408 : mask = vector8_add(mask, vector8_broadcast('0'));
229 2194408 : lo = vector8_add(lo, mask);
230 :
231 2194408 : hi = vector8_and(srcv, vector8_broadcast(0xf0));
232 2194408 : hi = vector8_shift_right(hi, 4);
233 2194408 : mask = vector8_gt(hi, vector8_broadcast(0x9));
234 2194408 : mask = vector8_and(mask, vector8_broadcast('a' - '0' - 10));
235 2194408 : mask = vector8_add(mask, vector8_broadcast('0'));
236 2194408 : hi = vector8_add(hi, mask);
237 :
238 2194408 : vector8_store((uint8 *) &dst[i * 2],
239 : vector8_interleave_low(hi, lo));
240 2194408 : vector8_store((uint8 *) &dst[i * 2 + sizeof(Vector8)],
241 : vector8_interleave_high(hi, lo));
242 : }
243 :
244 1399240 : (void) hex_encode_scalar(src + i, len - i, dst + i * 2);
245 :
246 1399240 : return (uint64) len * 2;
247 : #endif
248 : }
249 :
250 : static inline bool
251 77758 : get_hex(const char *cp, char *out)
252 : {
253 77758 : unsigned char c = (unsigned char) *cp;
254 77758 : int res = -1;
255 :
256 77758 : if (c < 127)
257 77758 : res = hexlookup[c];
258 :
259 77758 : *out = (char) res;
260 :
261 77758 : return (res >= 0);
262 : }
263 :
264 : uint64
265 32824 : hex_decode(const char *src, size_t len, char *dst)
266 : {
267 32824 : return hex_decode_safe(src, len, dst, NULL);
268 : }
269 :
270 : static inline uint64
271 144518 : hex_decode_safe_scalar(const char *src, size_t len, char *dst, Node *escontext)
272 : {
273 : const char *s,
274 : *srcend;
275 : char v1,
276 : v2,
277 : *p;
278 :
279 144518 : srcend = src + len;
280 144518 : s = src;
281 144518 : p = dst;
282 183604 : while (s < srcend)
283 : {
284 39164 : if (*s == ' ' || *s == '\n' || *s == '\t' || *s == '\r')
285 : {
286 252 : s++;
287 252 : continue;
288 : }
289 38912 : if (!get_hex(s, &v1))
290 48 : ereturn(escontext, 0,
291 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
292 : errmsg("invalid hexadecimal digit: \"%.*s\"",
293 : pg_mblen(s), s)));
294 38864 : s++;
295 38864 : if (s >= srcend)
296 18 : ereturn(escontext, 0,
297 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
298 : errmsg("invalid hexadecimal data: odd number of digits")));
299 38846 : if (!get_hex(s, &v2))
300 12 : ereturn(escontext, 0,
301 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
302 : errmsg("invalid hexadecimal digit: \"%.*s\"",
303 : pg_mblen(s), s)));
304 38834 : s++;
305 38834 : *p++ = (v1 << 4) | v2;
306 : }
307 :
308 144440 : return p - dst;
309 : }
310 :
311 : /*
312 : * This helper converts each byte to its binary-equivalent nibble by
313 : * subtraction and combines them to form the return bytes (separated by zero
314 : * bytes). Returns false if any input bytes are outside the expected ranges of
315 : * ASCII values. Otherwise, returns true.
316 : */
317 : #ifndef USE_NO_SIMD
318 : static inline bool
319 490896 : hex_decode_simd_helper(const Vector8 src, Vector8 *dst)
320 : {
321 : Vector8 sub;
322 490896 : Vector8 mask_hi = vector8_interleave_low(vector8_broadcast(0), vector8_broadcast(0x0f));
323 490896 : Vector8 mask_lo = vector8_interleave_low(vector8_broadcast(0x0f), vector8_broadcast(0));
324 : Vector8 tmp;
325 : bool ret;
326 :
327 490896 : tmp = vector8_gt(vector8_broadcast('9' + 1), src);
328 490896 : sub = vector8_and(tmp, vector8_broadcast('0'));
329 :
330 490896 : tmp = vector8_gt(src, vector8_broadcast('A' - 1));
331 490896 : tmp = vector8_and(tmp, vector8_broadcast('A' - 10));
332 490896 : sub = vector8_add(sub, tmp);
333 :
334 490896 : tmp = vector8_gt(src, vector8_broadcast('a' - 1));
335 490896 : tmp = vector8_and(tmp, vector8_broadcast('a' - 'A'));
336 490896 : sub = vector8_add(sub, tmp);
337 :
338 490896 : *dst = vector8_issub(src, sub);
339 490896 : ret = !vector8_has_ge(*dst, 0x10);
340 :
341 490896 : tmp = vector8_and(*dst, mask_hi);
342 490896 : tmp = vector8_shift_right(tmp, 8);
343 490896 : *dst = vector8_and(*dst, mask_lo);
344 490896 : *dst = vector8_shift_left(*dst, 4);
345 490896 : *dst = vector8_or(*dst, tmp);
346 490896 : return ret;
347 : }
348 : #endif /* ! USE_NO_SIMD */
349 :
350 : uint64
351 144518 : hex_decode_safe(const char *src, size_t len, char *dst, Node *escontext)
352 : {
353 : #ifdef USE_NO_SIMD
354 : return hex_decode_safe_scalar(src, len, dst, escontext);
355 : #else
356 144518 : const uint64 tail_idx = len & ~(sizeof(Vector8) * 2 - 1);
357 : uint64 i;
358 144518 : bool success = true;
359 :
360 : /*
361 : * We must process 2 vectors at a time since the output will be half the
362 : * length of the input.
363 : */
364 389966 : for (i = 0; i < tail_idx; i += sizeof(Vector8) * 2)
365 : {
366 : Vector8 srcv;
367 : Vector8 dstv1;
368 : Vector8 dstv2;
369 :
370 245448 : vector8_load(&srcv, (const uint8 *) &src[i]);
371 245448 : success &= hex_decode_simd_helper(srcv, &dstv1);
372 :
373 245448 : vector8_load(&srcv, (const uint8 *) &src[i + sizeof(Vector8)]);
374 245448 : success &= hex_decode_simd_helper(srcv, &dstv2);
375 :
376 245448 : vector8_store((uint8 *) &dst[i / 2], vector8_pack_16(dstv1, dstv2));
377 : }
378 :
379 : /*
380 : * If something didn't look right in the vector path, try again in the
381 : * scalar path so that we can handle it correctly.
382 : */
383 144518 : if (!success)
384 54 : i = 0;
385 :
386 144518 : return i / 2 + hex_decode_safe_scalar(src + i, len - i, dst + i / 2, escontext);
387 : #endif
388 : }
389 :
390 : static uint64
391 526290 : hex_enc_len(const char *src, size_t srclen)
392 : {
393 526290 : return (uint64) srclen << 1;
394 : }
395 :
396 : static uint64
397 32824 : hex_dec_len(const char *src, size_t srclen)
398 : {
399 32824 : return (uint64) srclen >> 1;
400 : }
401 :
402 : /*
403 : * BASE64 and BASE64URL
404 : */
405 :
406 : static const char _base64[] =
407 : "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
408 :
409 : static const char _base64url[] =
410 : "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
411 :
412 : static const int8 b64lookup[128] = {
413 : -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
414 : -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
415 : -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63,
416 : 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1,
417 : -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
418 : 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1,
419 : -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
420 : 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1,
421 : };
422 :
423 : /*
424 : * pg_base64_encode_internal
425 : *
426 : * Helper for decoding base64 or base64url. When url is passed as true the
427 : * input will be encoded using base64url. len bytes in src is encoded into
428 : * dst.
429 : */
430 : static uint64
431 114 : pg_base64_encode_internal(const char *src, size_t len, char *dst, bool url)
432 : {
433 : char *p,
434 114 : *lend = dst + 76;
435 : const char *s,
436 114 : *end = src + len;
437 114 : int pos = 2;
438 114 : uint32 buf = 0;
439 114 : const char *alphabet = url ? _base64url : _base64;
440 :
441 114 : s = src;
442 114 : p = dst;
443 :
444 1212 : while (s < end)
445 : {
446 1098 : buf |= (unsigned char) *s << (pos << 3);
447 1098 : pos--;
448 1098 : s++;
449 :
450 : /* write it out */
451 1098 : if (pos < 0)
452 : {
453 336 : *p++ = alphabet[(buf >> 18) & 0x3f];
454 336 : *p++ = alphabet[(buf >> 12) & 0x3f];
455 336 : *p++ = alphabet[(buf >> 6) & 0x3f];
456 336 : *p++ = alphabet[buf & 0x3f];
457 :
458 336 : pos = 2;
459 336 : buf = 0;
460 :
461 336 : if (!url && p >= lend)
462 : {
463 12 : *p++ = '\n';
464 12 : lend = p + 76;
465 : }
466 : }
467 : }
468 :
469 : /* Handle remaining bytes in buf */
470 114 : if (pos != 2)
471 : {
472 72 : *p++ = alphabet[(buf >> 18) & 0x3f];
473 72 : *p++ = alphabet[(buf >> 12) & 0x3f];
474 :
475 72 : if (pos == 0)
476 : {
477 18 : *p++ = alphabet[(buf >> 6) & 0x3f];
478 18 : if (!url)
479 0 : *p++ = '=';
480 : }
481 54 : else if (!url)
482 : {
483 12 : *p++ = '=';
484 12 : *p++ = '=';
485 : }
486 : }
487 :
488 114 : return p - dst;
489 : }
490 :
491 : static uint64
492 12 : pg_base64_encode(const char *src, size_t len, char *dst)
493 : {
494 12 : return pg_base64_encode_internal(src, len, dst, false);
495 : }
496 :
497 : static uint64
498 102 : pg_base64url_encode(const char *src, size_t len, char *dst)
499 : {
500 102 : return pg_base64_encode_internal(src, len, dst, true);
501 : }
502 :
503 : /*
504 : * pg_base64_decode_internal
505 : *
506 : * Helper for decoding base64 or base64url. When url is passed as true the
507 : * input will be assumed to be encoded using base64url.
508 : */
509 : static uint64
510 118 : pg_base64_decode_internal(const char *src, size_t len, char *dst, bool url)
511 : {
512 118 : const char *srcend = src + len,
513 118 : *s = src;
514 118 : char *p = dst;
515 : char c;
516 118 : int b = 0;
517 118 : uint32 buf = 0;
518 118 : int pos = 0,
519 118 : end = 0;
520 :
521 1140 : while (s < srcend)
522 : {
523 1034 : c = *s++;
524 :
525 1034 : if (c == ' ' || c == '\t' || c == '\n' || c == '\r')
526 6 : continue;
527 :
528 : /* convert base64url to base64 */
529 1028 : if (url)
530 : {
531 420 : if (c == '-')
532 18 : c = '+';
533 402 : else if (c == '_')
534 12 : c = '/';
535 : }
536 :
537 1028 : if (c == '=')
538 : {
539 : /* end sequence */
540 34 : if (!end)
541 : {
542 22 : if (pos == 2)
543 12 : end = 1;
544 10 : else if (pos == 3)
545 4 : end = 2;
546 : else
547 : {
548 : /* translator: %s is the name of an encoding scheme */
549 6 : ereport(ERROR,
550 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
551 : errmsg("unexpected \"=\" while decoding %s sequence", url ? "base64url" : "base64")));
552 : }
553 : }
554 28 : b = 0;
555 : }
556 : else
557 : {
558 994 : b = -1;
559 994 : if (c > 0 && c < 127)
560 994 : b = b64lookup[(unsigned char) c];
561 994 : if (b < 0)
562 : {
563 : /* translator: %s is the name of an encoding scheme */
564 6 : ereport(ERROR,
565 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
566 : errmsg("invalid symbol \"%.*s\" found while decoding %s sequence",
567 : pg_mblen(s - 1), s - 1,
568 : url ? "base64url" : "base64")));
569 : }
570 : }
571 : /* add it to buffer */
572 1016 : buf = (buf << 6) + b;
573 1016 : pos++;
574 1016 : if (pos == 4)
575 : {
576 218 : *p++ = (buf >> 16) & 255;
577 218 : if (end == 0 || end > 1)
578 206 : *p++ = (buf >> 8) & 255;
579 218 : if (end == 0 || end > 2)
580 202 : *p++ = buf & 255;
581 218 : buf = 0;
582 218 : pos = 0;
583 : }
584 : }
585 :
586 106 : if (pos == 2)
587 : {
588 36 : buf <<= 12;
589 36 : *p++ = (buf >> 16) & 0xFF;
590 : }
591 70 : else if (pos == 3)
592 : {
593 18 : buf <<= 6;
594 18 : *p++ = (buf >> 16) & 0xFF;
595 18 : *p++ = (buf >> 8) & 0xFF;
596 : }
597 52 : else if (pos != 0)
598 : {
599 : /* translator: %s is the name of an encoding scheme */
600 6 : ereport(ERROR,
601 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
602 : errmsg("invalid %s end sequence", url ? "base64url" : "base64"),
603 : errhint("Input data is missing padding, is truncated, or is otherwise corrupted.")));
604 : }
605 :
606 100 : return p - dst;
607 : }
608 :
609 : static uint64
610 10 : pg_base64_decode(const char *src, size_t len, char *dst)
611 : {
612 10 : return pg_base64_decode_internal(src, len, dst, false);
613 : }
614 :
615 : static uint64
616 108 : pg_base64url_decode(const char *src, size_t len, char *dst)
617 : {
618 108 : return pg_base64_decode_internal(src, len, dst, true);
619 : }
620 :
621 : static uint64
622 12 : pg_base64_enc_len(const char *src, size_t srclen)
623 : {
624 : /* 3 bytes will be converted to 4, linefeed after 76 chars */
625 12 : return ((uint64) srclen + 2) / 3 * 4 + (uint64) srclen / (76 * 3 / 4);
626 : }
627 :
628 : static uint64
629 10 : pg_base64_dec_len(const char *src, size_t srclen)
630 : {
631 10 : return ((uint64) srclen * 3) >> 2;
632 : }
633 :
634 : static uint64
635 102 : pg_base64url_enc_len(const char *src, size_t srclen)
636 : {
637 : /*
638 : * Unlike standard base64, base64url doesn't use padding characters when
639 : * the input length is not divisible by 3
640 : */
641 102 : return (srclen + 2) / 3 * 4;
642 : }
643 :
644 : static uint64
645 108 : pg_base64url_dec_len(const char *src, size_t srclen)
646 : {
647 : /*
648 : * For base64, each 4 characters of input produce at most 3 bytes of
649 : * output. For base64url without padding, we need to round up to the
650 : * nearest 4
651 : */
652 108 : size_t adjusted_len = srclen;
653 :
654 108 : if (srclen % 4 != 0)
655 60 : adjusted_len += 4 - (srclen % 4);
656 :
657 108 : return (adjusted_len * 3) / 4;
658 : }
659 :
660 : /*
661 : * Escape
662 : * Minimally escape bytea to text.
663 : * De-escape text to bytea.
664 : *
665 : * We must escape zero bytes and high-bit-set bytes to avoid generating
666 : * text that might be invalid in the current encoding, or that might
667 : * change to something else if passed through an encoding conversion
668 : * (leading to failing to de-escape to the original bytea value).
669 : * Also of course backslash itself has to be escaped.
670 : *
671 : * De-escaping processes \\ and any \### octal
672 : */
673 :
674 : #define VAL(CH) ((CH) - '0')
675 : #define DIG(VAL) ((VAL) + '0')
676 :
677 : static uint64
678 68 : esc_encode(const char *src, size_t srclen, char *dst)
679 : {
680 68 : const char *end = src + srclen;
681 68 : char *rp = dst;
682 68 : uint64 len = 0;
683 :
684 656 : while (src < end)
685 : {
686 588 : unsigned char c = (unsigned char) *src;
687 :
688 588 : if (c == '\0' || IS_HIGHBIT_SET(c))
689 : {
690 90 : rp[0] = '\\';
691 90 : rp[1] = DIG(c >> 6);
692 90 : rp[2] = DIG((c >> 3) & 7);
693 90 : rp[3] = DIG(c & 7);
694 90 : rp += 4;
695 90 : len += 4;
696 : }
697 498 : else if (c == '\\')
698 : {
699 0 : rp[0] = '\\';
700 0 : rp[1] = '\\';
701 0 : rp += 2;
702 0 : len += 2;
703 : }
704 : else
705 : {
706 498 : *rp++ = c;
707 498 : len++;
708 : }
709 :
710 588 : src++;
711 : }
712 :
713 68 : return len;
714 : }
715 :
716 : static uint64
717 30 : esc_decode(const char *src, size_t srclen, char *dst)
718 : {
719 30 : const char *end = src + srclen;
720 30 : char *rp = dst;
721 30 : uint64 len = 0;
722 :
723 2400084 : while (src < end)
724 : {
725 2400054 : if (src[0] != '\\')
726 2400024 : *rp++ = *src++;
727 30 : else if (src + 3 < end &&
728 30 : (src[1] >= '0' && src[1] <= '3') &&
729 30 : (src[2] >= '0' && src[2] <= '7') &&
730 30 : (src[3] >= '0' && src[3] <= '7'))
731 30 : {
732 : int val;
733 :
734 30 : val = VAL(src[1]);
735 30 : val <<= 3;
736 30 : val += VAL(src[2]);
737 30 : val <<= 3;
738 30 : *rp++ = val + VAL(src[3]);
739 30 : src += 4;
740 : }
741 0 : else if (src + 1 < end &&
742 0 : (src[1] == '\\'))
743 : {
744 0 : *rp++ = '\\';
745 0 : src += 2;
746 : }
747 : else
748 : {
749 : /*
750 : * One backslash, not followed by ### valid octal. Should never
751 : * get here, since esc_dec_len does same check.
752 : */
753 0 : ereport(ERROR,
754 : (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
755 : errmsg("invalid input syntax for type %s", "bytea")));
756 : }
757 :
758 2400054 : len++;
759 : }
760 :
761 30 : return len;
762 : }
763 :
764 : static uint64
765 68 : esc_enc_len(const char *src, size_t srclen)
766 : {
767 68 : const char *end = src + srclen;
768 68 : uint64 len = 0;
769 :
770 656 : while (src < end)
771 : {
772 588 : if (*src == '\0' || IS_HIGHBIT_SET(*src))
773 90 : len += 4;
774 498 : else if (*src == '\\')
775 0 : len += 2;
776 : else
777 498 : len++;
778 :
779 588 : src++;
780 : }
781 :
782 68 : return len;
783 : }
784 :
785 : static uint64
786 30 : esc_dec_len(const char *src, size_t srclen)
787 : {
788 30 : const char *end = src + srclen;
789 30 : uint64 len = 0;
790 :
791 2400084 : while (src < end)
792 : {
793 2400054 : if (src[0] != '\\')
794 2400024 : src++;
795 30 : else if (src + 3 < end &&
796 30 : (src[1] >= '0' && src[1] <= '3') &&
797 30 : (src[2] >= '0' && src[2] <= '7') &&
798 30 : (src[3] >= '0' && src[3] <= '7'))
799 : {
800 : /*
801 : * backslash + valid octal
802 : */
803 30 : src += 4;
804 : }
805 0 : else if (src + 1 < end &&
806 0 : (src[1] == '\\'))
807 : {
808 : /*
809 : * two backslashes = backslash
810 : */
811 0 : src += 2;
812 : }
813 : else
814 : {
815 : /*
816 : * one backslash, not followed by ### valid octal
817 : */
818 0 : ereport(ERROR,
819 : (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
820 : errmsg("invalid input syntax for type %s", "bytea")));
821 : }
822 :
823 2400054 : len++;
824 : }
825 30 : return len;
826 : }
827 :
828 : /*
829 : * Common
830 : */
831 :
832 : static const struct
833 : {
834 : const char *name;
835 : struct pg_encoding enc;
836 : } enclist[] =
837 :
838 : {
839 : {
840 : "hex",
841 : {
842 : hex_enc_len, hex_dec_len, hex_encode, hex_decode
843 : }
844 : },
845 : {
846 : "base64",
847 : {
848 : pg_base64_enc_len, pg_base64_dec_len, pg_base64_encode, pg_base64_decode
849 : }
850 : },
851 : {
852 : "base64url",
853 : {
854 : pg_base64url_enc_len, pg_base64url_dec_len, pg_base64url_encode, pg_base64url_decode
855 : }
856 : },
857 : {
858 : "escape",
859 : {
860 : esc_enc_len, esc_dec_len, esc_encode, esc_decode
861 : }
862 : },
863 : {
864 : NULL,
865 : {
866 : NULL, NULL, NULL, NULL
867 : }
868 : }
869 : };
870 :
871 : static const struct pg_encoding *
872 559456 : pg_find_encoding(const char *name)
873 : {
874 : int i;
875 :
876 560240 : for (i = 0; enclist[i].name; i++)
877 560228 : if (pg_strcasecmp(enclist[i].name, name) == 0)
878 559444 : return &enclist[i].enc;
879 :
880 12 : return NULL;
881 : }
|