LCOV - code coverage report
Current view: top level - src/backend/utils/adt - encode.c (source / functions) Coverage Total Hit
Test: PostgreSQL 19devel Lines: 93.8 % 307 288
Test Date: 2026-03-01 15:14:58 Functions: 100.0 % 26 26
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-2026, 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       263049 : binary_encode(PG_FUNCTION_ARGS)
      50              : {
      51       263049 :     bytea      *data = PG_GETARG_BYTEA_PP(0);
      52       263049 :     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       263049 :     namebuf = TextDatumGetCString(name);
      62              : 
      63       263049 :     enc = pg_find_encoding(namebuf);
      64       263049 :     if (enc == NULL)
      65            3 :         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       263046 :     dataptr = VARDATA_ANY(data);
      72       263046 :     datalen = VARSIZE_ANY_EXHDR(data);
      73              : 
      74       263046 :     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       263046 :     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       263046 :     result = palloc(VARHDRSZ + resultlen);
      86              : 
      87       263046 :     res = enc->encode(dataptr, datalen, VARDATA(result));
      88              : 
      89              :     /* Make this FATAL 'cause we've trodden on memory ... */
      90       263046 :     if (res > resultlen)
      91            0 :         elog(FATAL, "overflow - encode estimate too small");
      92              : 
      93       263046 :     SET_VARSIZE(result, VARHDRSZ + res);
      94              : 
      95       263046 :     PG_RETURN_TEXT_P(result);
      96              : }
      97              : 
      98              : Datum
      99        16489 : binary_decode(PG_FUNCTION_ARGS)
     100              : {
     101        16489 :     text       *data = PG_GETARG_TEXT_PP(0);
     102        16489 :     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        16489 :     namebuf = TextDatumGetCString(name);
     112              : 
     113        16489 :     enc = pg_find_encoding(namebuf);
     114        16489 :     if (enc == NULL)
     115            3 :         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        16486 :     dataptr = VARDATA_ANY(data);
     122        16486 :     datalen = VARSIZE_ANY_EXHDR(data);
     123              : 
     124        16486 :     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        16486 :     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        16486 :     result = palloc(VARHDRSZ + resultlen);
     136              : 
     137        16486 :     res = enc->decode(dataptr, datalen, VARDATA(result));
     138              : 
     139              :     /* Make this FATAL 'cause we've trodden on memory ... */
     140        16477 :     if (res > resultlen)
     141            0 :         elog(FATAL, "overflow - decode estimate too small");
     142              : 
     143        16477 :     SET_VARSIZE(result, VARHDRSZ + res);
     144              : 
     145        16477 :     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       706310 : hex_encode_scalar(const char *src, size_t len, char *dst)
     187              : {
     188       706310 :     const char *end = src + len;
     189              : 
     190      1386633 :     while (src < end)
     191              :     {
     192       680323 :         unsigned char usrc = *((const unsigned char *) src);
     193              : 
     194       680323 :         memcpy(dst, &hextbl[2 * usrc], 2);
     195       680323 :         src++;
     196       680323 :         dst += 2;
     197              :     }
     198       706310 :     return (uint64) len * 2;
     199              : }
     200              : 
     201              : uint64
     202       706310 : 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       706310 :     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      1803645 :     for (i = 0; i < tail_idx; i += sizeof(Vector8))
     217              :     {
     218              :         Vector8     srcv;
     219              :         Vector8     lo;
     220              :         Vector8     hi;
     221              :         Vector8     mask;
     222              : 
     223      1097335 :         vector8_load(&srcv, (const uint8 *) &src[i]);
     224              : 
     225      1097335 :         lo = vector8_and(srcv, vector8_broadcast(0x0f));
     226      1097335 :         mask = vector8_gt(lo, vector8_broadcast(0x9));
     227      1097335 :         mask = vector8_and(mask, vector8_broadcast('a' - '0' - 10));
     228      1097335 :         mask = vector8_add(mask, vector8_broadcast('0'));
     229      1097335 :         lo = vector8_add(lo, mask);
     230              : 
     231      1097335 :         hi = vector8_and(srcv, vector8_broadcast(0xf0));
     232      1097335 :         hi = vector8_shift_right(hi, 4);
     233      1097335 :         mask = vector8_gt(hi, vector8_broadcast(0x9));
     234      1097335 :         mask = vector8_and(mask, vector8_broadcast('a' - '0' - 10));
     235      1097335 :         mask = vector8_add(mask, vector8_broadcast('0'));
     236      1097335 :         hi = vector8_add(hi, mask);
     237              : 
     238      1097335 :         vector8_store((uint8 *) &dst[i * 2],
     239              :                       vector8_interleave_low(hi, lo));
     240      1097335 :         vector8_store((uint8 *) &dst[i * 2 + sizeof(Vector8)],
     241              :                       vector8_interleave_high(hi, lo));
     242              :     }
     243              : 
     244       706310 :     (void) hex_encode_scalar(src + i, len - i, dst + i * 2);
     245              : 
     246       706310 :     return (uint64) len * 2;
     247              : #endif
     248              : }
     249              : 
     250              : static inline bool
     251        42751 : get_hex(const char *cp, char *out)
     252              : {
     253        42751 :     unsigned char c = (unsigned char) *cp;
     254        42751 :     int         res = -1;
     255              : 
     256        42751 :     if (c < 127)
     257        42751 :         res = hexlookup[c];
     258              : 
     259        42751 :     *out = (char) res;
     260              : 
     261        42751 :     return (res >= 0);
     262              : }
     263              : 
     264              : uint64
     265        16412 : hex_decode(const char *src, size_t len, char *dst)
     266              : {
     267        16412 :     return hex_decode_safe(src, len, dst, NULL);
     268              : }
     269              : 
     270              : static inline uint64
     271        72522 : 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        72522 :     srcend = src + len;
     280        72522 :     s = src;
     281        72522 :     p = dst;
     282        94041 :     while (s < srcend)
     283              :     {
     284        21558 :         if (*s == ' ' || *s == '\n' || *s == '\t' || *s == '\r')
     285              :         {
     286          166 :             s++;
     287          166 :             continue;
     288              :         }
     289        21392 :         if (!get_hex(s, &v1))
     290           24 :             ereturn(escontext, 0,
     291              :                     (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     292              :                      errmsg("invalid hexadecimal digit: \"%.*s\"",
     293              :                             pg_mblen_range(s, srcend), s)));
     294        21368 :         s++;
     295        21368 :         if (s >= srcend)
     296            9 :             ereturn(escontext, 0,
     297              :                     (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     298              :                      errmsg("invalid hexadecimal data: odd number of digits")));
     299        21359 :         if (!get_hex(s, &v2))
     300            6 :             ereturn(escontext, 0,
     301              :                     (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     302              :                      errmsg("invalid hexadecimal digit: \"%.*s\"",
     303              :                             pg_mblen_range(s, srcend), s)));
     304        21353 :         s++;
     305        21353 :         *p++ = (v1 << 4) | v2;
     306              :     }
     307              : 
     308        72483 :     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       246302 : hex_decode_simd_helper(const Vector8 src, Vector8 *dst)
     320              : {
     321              :     Vector8     sub;
     322       246302 :     Vector8     mask_hi = vector8_interleave_low(vector8_broadcast(0), vector8_broadcast(0x0f));
     323       246302 :     Vector8     mask_lo = vector8_interleave_low(vector8_broadcast(0x0f), vector8_broadcast(0));
     324              :     Vector8     tmp;
     325              :     bool        ret;
     326              : 
     327       246302 :     tmp = vector8_gt(vector8_broadcast('9' + 1), src);
     328       246302 :     sub = vector8_and(tmp, vector8_broadcast('0'));
     329              : 
     330       246302 :     tmp = vector8_gt(src, vector8_broadcast('A' - 1));
     331       246302 :     tmp = vector8_and(tmp, vector8_broadcast('A' - 10));
     332       246302 :     sub = vector8_add(sub, tmp);
     333              : 
     334       246302 :     tmp = vector8_gt(src, vector8_broadcast('a' - 1));
     335       246302 :     tmp = vector8_and(tmp, vector8_broadcast('a' - 'A'));
     336       246302 :     sub = vector8_add(sub, tmp);
     337              : 
     338       246302 :     *dst = vector8_issub(src, sub);
     339       246302 :     ret = !vector8_has_ge(*dst, 0x10);
     340              : 
     341       246302 :     tmp = vector8_and(*dst, mask_hi);
     342       246302 :     tmp = vector8_shift_right(tmp, 8);
     343       246302 :     *dst = vector8_and(*dst, mask_lo);
     344       246302 :     *dst = vector8_shift_left(*dst, 4);
     345       246302 :     *dst = vector8_or(*dst, tmp);
     346       246302 :     return ret;
     347              : }
     348              : #endif                          /* ! USE_NO_SIMD */
     349              : 
     350              : uint64
     351        72522 : 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        72522 :     const uint64 tail_idx = len & ~(sizeof(Vector8) * 2 - 1);
     357              :     uint64      i;
     358        72522 :     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       195673 :     for (i = 0; i < tail_idx; i += sizeof(Vector8) * 2)
     365              :     {
     366              :         Vector8     srcv;
     367              :         Vector8     dstv1;
     368              :         Vector8     dstv2;
     369              : 
     370       123151 :         vector8_load(&srcv, (const uint8 *) &src[i]);
     371       123151 :         success &= hex_decode_simd_helper(srcv, &dstv1);
     372              : 
     373       123151 :         vector8_load(&srcv, (const uint8 *) &src[i + sizeof(Vector8)]);
     374       123151 :         success &= hex_decode_simd_helper(srcv, &dstv2);
     375              : 
     376       123151 :         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        72522 :     if (!success)
     384           29 :         i = 0;
     385              : 
     386        72522 :     return i / 2 + hex_decode_safe_scalar(src + i, len - i, dst + i / 2, escontext);
     387              : #endif
     388              : }
     389              : 
     390              : static uint64
     391       262955 : hex_enc_len(const char *src, size_t srclen)
     392              : {
     393       262955 :     return (uint64) srclen << 1;
     394              : }
     395              : 
     396              : static uint64
     397        16412 : hex_dec_len(const char *src, size_t srclen)
     398              : {
     399        16412 :     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           57 : pg_base64_encode_internal(const char *src, size_t len, char *dst, bool url)
     432              : {
     433              :     char       *p,
     434           57 :                *lend = dst + 76;
     435              :     const char *s,
     436           57 :                *end = src + len;
     437           57 :     int         pos = 2;
     438           57 :     uint32      buf = 0;
     439           57 :     const char *alphabet = url ? _base64url : _base64;
     440              : 
     441           57 :     s = src;
     442           57 :     p = dst;
     443              : 
     444          606 :     while (s < end)
     445              :     {
     446          549 :         buf |= (unsigned char) *s << (pos << 3);
     447          549 :         pos--;
     448          549 :         s++;
     449              : 
     450              :         /* write it out */
     451          549 :         if (pos < 0)
     452              :         {
     453          168 :             *p++ = alphabet[(buf >> 18) & 0x3f];
     454          168 :             *p++ = alphabet[(buf >> 12) & 0x3f];
     455          168 :             *p++ = alphabet[(buf >> 6) & 0x3f];
     456          168 :             *p++ = alphabet[buf & 0x3f];
     457              : 
     458          168 :             pos = 2;
     459          168 :             buf = 0;
     460              : 
     461          168 :             if (!url && p >= lend)
     462              :             {
     463            6 :                 *p++ = '\n';
     464            6 :                 lend = p + 76;
     465              :             }
     466              :         }
     467              :     }
     468              : 
     469              :     /* Handle remaining bytes in buf */
     470           57 :     if (pos != 2)
     471              :     {
     472           36 :         *p++ = alphabet[(buf >> 18) & 0x3f];
     473           36 :         *p++ = alphabet[(buf >> 12) & 0x3f];
     474              : 
     475           36 :         if (pos == 0)
     476              :         {
     477            9 :             *p++ = alphabet[(buf >> 6) & 0x3f];
     478            9 :             if (!url)
     479            0 :                 *p++ = '=';
     480              :         }
     481           27 :         else if (!url)
     482              :         {
     483            6 :             *p++ = '=';
     484            6 :             *p++ = '=';
     485              :         }
     486              :     }
     487              : 
     488           57 :     return p - dst;
     489              : }
     490              : 
     491              : static uint64
     492            6 : pg_base64_encode(const char *src, size_t len, char *dst)
     493              : {
     494            6 :     return pg_base64_encode_internal(src, len, dst, false);
     495              : }
     496              : 
     497              : static uint64
     498           51 : pg_base64url_encode(const char *src, size_t len, char *dst)
     499              : {
     500           51 :     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           59 : pg_base64_decode_internal(const char *src, size_t len, char *dst, bool url)
     511              : {
     512           59 :     const char *srcend = src + len,
     513           59 :                *s = src;
     514           59 :     char       *p = dst;
     515              :     char        c;
     516           59 :     int         b = 0;
     517           59 :     uint32      buf = 0;
     518           59 :     int         pos = 0,
     519           59 :                 end = 0;
     520              : 
     521          570 :     while (s < srcend)
     522              :     {
     523          517 :         c = *s++;
     524              : 
     525          517 :         if (c == ' ' || c == '\t' || c == '\n' || c == '\r')
     526            3 :             continue;
     527              : 
     528              :         /* convert base64url to base64 */
     529          514 :         if (url)
     530              :         {
     531          210 :             if (c == '-')
     532            9 :                 c = '+';
     533          201 :             else if (c == '_')
     534            6 :                 c = '/';
     535              :         }
     536              : 
     537          514 :         if (c == '=')
     538              :         {
     539              :             /* end sequence */
     540           17 :             if (!end)
     541              :             {
     542           11 :                 if (pos == 2)
     543            6 :                     end = 1;
     544            5 :                 else if (pos == 3)
     545            2 :                     end = 2;
     546              :                 else
     547              :                 {
     548              :                     /* translator: %s is the name of an encoding scheme */
     549            3 :                     ereport(ERROR,
     550              :                             (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     551              :                              errmsg("unexpected \"=\" while decoding %s sequence", url ? "base64url" : "base64")));
     552              :                 }
     553              :             }
     554           14 :             b = 0;
     555              :         }
     556              :         else
     557              :         {
     558          497 :             b = -1;
     559          497 :             if (c > 0 && c < 127)
     560          497 :                 b = b64lookup[(unsigned char) c];
     561          497 :             if (b < 0)
     562              :             {
     563              :                 /* translator: %s is the name of an encoding scheme */
     564            3 :                 ereport(ERROR,
     565              :                         (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     566              :                          errmsg("invalid symbol \"%.*s\" found while decoding %s sequence",
     567              :                                 pg_mblen_range(s - 1, srcend), s - 1,
     568              :                                 url ? "base64url" : "base64")));
     569              :             }
     570              :         }
     571              :         /* add it to buffer */
     572          508 :         buf = (buf << 6) + b;
     573          508 :         pos++;
     574          508 :         if (pos == 4)
     575              :         {
     576          109 :             *p++ = (buf >> 16) & 255;
     577          109 :             if (end == 0 || end > 1)
     578          103 :                 *p++ = (buf >> 8) & 255;
     579          109 :             if (end == 0 || end > 2)
     580          101 :                 *p++ = buf & 255;
     581          109 :             buf = 0;
     582          109 :             pos = 0;
     583              :         }
     584              :     }
     585              : 
     586           53 :     if (pos == 2)
     587              :     {
     588           18 :         buf <<= 12;
     589           18 :         *p++ = (buf >> 16) & 0xFF;
     590              :     }
     591           35 :     else if (pos == 3)
     592              :     {
     593            9 :         buf <<= 6;
     594            9 :         *p++ = (buf >> 16) & 0xFF;
     595            9 :         *p++ = (buf >> 8) & 0xFF;
     596              :     }
     597           26 :     else if (pos != 0)
     598              :     {
     599              :         /* translator: %s is the name of an encoding scheme */
     600            3 :         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           50 :     return p - dst;
     607              : }
     608              : 
     609              : static uint64
     610            5 : pg_base64_decode(const char *src, size_t len, char *dst)
     611              : {
     612            5 :     return pg_base64_decode_internal(src, len, dst, false);
     613              : }
     614              : 
     615              : static uint64
     616           54 : pg_base64url_decode(const char *src, size_t len, char *dst)
     617              : {
     618           54 :     return pg_base64_decode_internal(src, len, dst, true);
     619              : }
     620              : 
     621              : static uint64
     622            6 : pg_base64_enc_len(const char *src, size_t srclen)
     623              : {
     624              :     /* 3 bytes will be converted to 4, linefeed after 76 chars */
     625            6 :     return ((uint64) srclen + 2) / 3 * 4 + (uint64) srclen / (76 * 3 / 4);
     626              : }
     627              : 
     628              : static uint64
     629            5 : pg_base64_dec_len(const char *src, size_t srclen)
     630              : {
     631            5 :     return ((uint64) srclen * 3) >> 2;
     632              : }
     633              : 
     634              : static uint64
     635           51 : 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           51 :     return (srclen + 2) / 3 * 4;
     642              : }
     643              : 
     644              : static uint64
     645           54 : 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           54 :     size_t      adjusted_len = srclen;
     653              : 
     654           54 :     if (srclen % 4 != 0)
     655           30 :         adjusted_len += 4 - (srclen % 4);
     656              : 
     657           54 :     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           34 : esc_encode(const char *src, size_t srclen, char *dst)
     679              : {
     680           34 :     const char *end = src + srclen;
     681           34 :     char       *rp = dst;
     682           34 :     uint64      len = 0;
     683              : 
     684          328 :     while (src < end)
     685              :     {
     686          294 :         unsigned char c = (unsigned char) *src;
     687              : 
     688          294 :         if (c == '\0' || IS_HIGHBIT_SET(c))
     689              :         {
     690           48 :             rp[0] = '\\';
     691           48 :             rp[1] = DIG(c >> 6);
     692           48 :             rp[2] = DIG((c >> 3) & 7);
     693           48 :             rp[3] = DIG(c & 7);
     694           48 :             rp += 4;
     695           48 :             len += 4;
     696              :         }
     697          246 :         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          246 :             *rp++ = c;
     707          246 :             len++;
     708              :         }
     709              : 
     710          294 :         src++;
     711              :     }
     712              : 
     713           34 :     return len;
     714              : }
     715              : 
     716              : static uint64
     717           15 : esc_decode(const char *src, size_t srclen, char *dst)
     718              : {
     719           15 :     const char *end = src + srclen;
     720           15 :     char       *rp = dst;
     721           15 :     uint64      len = 0;
     722              : 
     723      1200042 :     while (src < end)
     724              :     {
     725      1200027 :         if (src[0] != '\\')
     726      1200012 :             *rp++ = *src++;
     727           15 :         else if (src + 3 < end &&
     728           15 :                  (src[1] >= '0' && src[1] <= '3') &&
     729           15 :                  (src[2] >= '0' && src[2] <= '7') &&
     730           15 :                  (src[3] >= '0' && src[3] <= '7'))
     731           15 :         {
     732              :             int         val;
     733              : 
     734           15 :             val = VAL(src[1]);
     735           15 :             val <<= 3;
     736           15 :             val += VAL(src[2]);
     737           15 :             val <<= 3;
     738           15 :             *rp++ = val + VAL(src[3]);
     739           15 :             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      1200027 :         len++;
     759              :     }
     760              : 
     761           15 :     return len;
     762              : }
     763              : 
     764              : static uint64
     765           34 : esc_enc_len(const char *src, size_t srclen)
     766              : {
     767           34 :     const char *end = src + srclen;
     768           34 :     uint64      len = 0;
     769              : 
     770          328 :     while (src < end)
     771              :     {
     772          294 :         if (*src == '\0' || IS_HIGHBIT_SET(*src))
     773           48 :             len += 4;
     774          246 :         else if (*src == '\\')
     775            0 :             len += 2;
     776              :         else
     777          246 :             len++;
     778              : 
     779          294 :         src++;
     780              :     }
     781              : 
     782           34 :     return len;
     783              : }
     784              : 
     785              : static uint64
     786           15 : esc_dec_len(const char *src, size_t srclen)
     787              : {
     788           15 :     const char *end = src + srclen;
     789           15 :     uint64      len = 0;
     790              : 
     791      1200042 :     while (src < end)
     792              :     {
     793      1200027 :         if (src[0] != '\\')
     794      1200012 :             src++;
     795           15 :         else if (src + 3 < end &&
     796           15 :                  (src[1] >= '0' && src[1] <= '3') &&
     797           15 :                  (src[2] >= '0' && src[2] <= '7') &&
     798           15 :                  (src[3] >= '0' && src[3] <= '7'))
     799              :         {
     800              :             /*
     801              :              * backslash + valid octal
     802              :              */
     803           15 :             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      1200027 :         len++;
     824              :     }
     825           15 :     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       279538 : pg_find_encoding(const char *name)
     873              : {
     874              :     int         i;
     875              : 
     876       279930 :     for (i = 0; enclist[i].name; i++)
     877       279924 :         if (pg_strcasecmp(enclist[i].name, name) == 0)
     878       279532 :             return &enclist[i].enc;
     879              : 
     880            6 :     return NULL;
     881              : }
        

Generated by: LCOV version 2.0-1