LCOV - code coverage report
Current view: top level - src/backend/utils/adt - encode.c (source / functions) Hit Total Coverage
Test: PostgreSQL 19devel Lines: 285 307 92.8 %
Date: 2025-10-24 12:17:48 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      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             : }

Generated by: LCOV version 1.16