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