LCOV - code coverage report
Current view: top level - src/backend/utils/adt - encode.c (source / functions) Hit Total Coverage
Test: PostgreSQL 19devel Lines: 288 307 93.8 %
Date: 2025-12-03 05:18:44 Functions: 26 26 100.0 %
Legend: Lines: hit not hit

          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             : }

Generated by: LCOV version 1.16