Coverage Report

Created: 2026-09-28 06:55

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/postgres/src/backend/utils/adt/encode.c
Line
Count
Source
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
binary_encode(PG_FUNCTION_ARGS)
50
0
{
51
0
  bytea    *data = PG_GETARG_BYTEA_PP(0);
52
0
  Datum   name = PG_GETARG_DATUM(1);
53
0
  text     *result;
54
0
  char     *namebuf;
55
0
  char     *dataptr;
56
0
  size_t    datalen;
57
0
  uint64    resultlen;
58
0
  uint64    res;
59
0
  const struct pg_encoding *enc;
60
61
0
  namebuf = TextDatumGetCString(name);
62
63
0
  enc = pg_find_encoding(namebuf);
64
0
  if (enc == NULL)
65
0
    ereport(ERROR,
66
0
        (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
67
0
         errmsg("unrecognized encoding: \"%s\"", namebuf),
68
0
         errhint("Valid encodings are \"%s\", \"%s\", \"%s\", \"%s\", and \"%s\".",
69
0
             "base32hex", "base64", "base64url", "escape", "hex")));
70
71
0
  dataptr = VARDATA_ANY(data);
72
0
  datalen = VARSIZE_ANY_EXHDR(data);
73
74
0
  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
0
  if (resultlen > MaxAllocSize - VARHDRSZ)
81
0
    ereport(ERROR,
82
0
        (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
83
0
         errmsg("result of encoding conversion is too large")));
84
85
0
  result = palloc(VARHDRSZ + resultlen);
86
87
0
  res = enc->encode(dataptr, datalen, VARDATA(result));
88
89
  /* Make this FATAL 'cause we've trodden on memory ... */
90
0
  if (res > resultlen)
91
0
    elog(FATAL, "overflow - encode estimate too small");
92
93
0
  SET_VARSIZE(result, VARHDRSZ + res);
94
95
0
  PG_RETURN_TEXT_P(result);
96
0
}
97
98
Datum
99
binary_decode(PG_FUNCTION_ARGS)
100
0
{
101
0
  text     *data = PG_GETARG_TEXT_PP(0);
102
0
  Datum   name = PG_GETARG_DATUM(1);
103
0
  bytea    *result;
104
0
  char     *namebuf;
105
0
  char     *dataptr;
106
0
  size_t    datalen;
107
0
  uint64    resultlen;
108
0
  uint64    res;
109
0
  const struct pg_encoding *enc;
110
111
0
  namebuf = TextDatumGetCString(name);
112
113
0
  enc = pg_find_encoding(namebuf);
114
0
  if (enc == NULL)
115
0
    ereport(ERROR,
116
0
        (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
117
0
         errmsg("unrecognized encoding: \"%s\"", namebuf),
118
0
         errhint("Valid encodings are \"%s\", \"%s\", \"%s\", \"%s\", and \"%s\".",
119
0
             "base32hex", "base64", "base64url", "escape", "hex")));
120
121
0
  dataptr = VARDATA_ANY(data);
122
0
  datalen = VARSIZE_ANY_EXHDR(data);
123
124
0
  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
0
  if (resultlen > MaxAllocSize - VARHDRSZ)
131
0
    ereport(ERROR,
132
0
        (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
133
0
         errmsg("result of decoding conversion is too large")));
134
135
0
  result = palloc(VARHDRSZ + resultlen);
136
137
0
  res = enc->decode(dataptr, datalen, VARDATA(result));
138
139
  /* Make this FATAL 'cause we've trodden on memory ... */
140
0
  if (res > resultlen)
141
0
    elog(FATAL, "overflow - decode estimate too small");
142
143
0
  SET_VARSIZE(result, VARHDRSZ + res);
144
145
0
  PG_RETURN_BYTEA_P(result);
146
0
}
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
hex_encode_scalar(const char *src, size_t len, char *dst)
187
0
{
188
0
  const char *end = src + len;
189
190
0
  while (src < end)
191
0
  {
192
0
    unsigned char usrc = *((const unsigned char *) src);
193
194
0
    memcpy(dst, &hextbl[2 * usrc], 2);
195
0
    src++;
196
0
    dst += 2;
197
0
  }
198
0
  return (uint64) len * 2;
199
0
}
200
201
uint64
202
hex_encode(const char *src, size_t len, char *dst)
203
0
{
204
#ifdef USE_NO_SIMD
205
  return hex_encode_scalar(src, len, dst);
206
#else
207
0
  const uint64 tail_idx = len & ~(sizeof(Vector8) - 1);
208
0
  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
0
  for (i = 0; i < tail_idx; i += sizeof(Vector8))
217
0
  {
218
0
    Vector8   srcv;
219
0
    Vector8   lo;
220
0
    Vector8   hi;
221
0
    Vector8   mask;
222
223
0
    vector8_load(&srcv, (const uint8 *) &src[i]);
224
225
0
    lo = vector8_and(srcv, vector8_broadcast(0x0f));
226
0
    mask = vector8_gt(lo, vector8_broadcast(0x9));
227
0
    mask = vector8_and(mask, vector8_broadcast('a' - '0' - 10));
228
0
    mask = vector8_add(mask, vector8_broadcast('0'));
229
0
    lo = vector8_add(lo, mask);
230
231
0
    hi = vector8_and(srcv, vector8_broadcast(0xf0));
232
0
    hi = vector8_shift_right(hi, 4);
233
0
    mask = vector8_gt(hi, vector8_broadcast(0x9));
234
0
    mask = vector8_and(mask, vector8_broadcast('a' - '0' - 10));
235
0
    mask = vector8_add(mask, vector8_broadcast('0'));
236
0
    hi = vector8_add(hi, mask);
237
238
0
    vector8_store((uint8 *) &dst[i * 2],
239
0
            vector8_interleave_low(hi, lo));
240
0
    vector8_store((uint8 *) &dst[i * 2 + sizeof(Vector8)],
241
0
            vector8_interleave_high(hi, lo));
242
0
  }
243
244
0
  (void) hex_encode_scalar(src + i, len - i, dst + i * 2);
245
246
0
  return (uint64) len * 2;
247
0
#endif
248
0
}
249
250
static inline bool
251
get_hex(const char *cp, char *out)
252
0
{
253
0
  unsigned char c = (unsigned char) *cp;
254
0
  int     res = -1;
255
256
0
  if (c < 127)
257
0
    res = hexlookup[c];
258
259
0
  *out = (char) res;
260
261
0
  return (res >= 0);
262
0
}
263
264
uint64
265
hex_decode(const char *src, size_t len, char *dst)
266
0
{
267
0
  return hex_decode_safe(src, len, dst, NULL);
268
0
}
269
270
static inline uint64
271
hex_decode_safe_scalar(const char *src, size_t len, char *dst, Node *escontext)
272
0
{
273
0
  const char *s,
274
0
         *srcend;
275
0
  char    v1,
276
0
        v2,
277
0
         *p;
278
279
0
  srcend = src + len;
280
0
  s = src;
281
0
  p = dst;
282
0
  while (s < srcend)
283
0
  {
284
0
    if (*s == ' ' || *s == '\n' || *s == '\t' || *s == '\r')
285
0
    {
286
0
      s++;
287
0
      continue;
288
0
    }
289
0
    if (!get_hex(s, &v1))
290
0
      ereturn(escontext, 0,
291
0
          (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
292
0
           errmsg("invalid hexadecimal digit: \"%.*s\"",
293
0
              pg_mblen_range(s, srcend), s)));
294
0
    s++;
295
0
    if (s >= srcend)
296
0
      ereturn(escontext, 0,
297
0
          (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
298
0
           errmsg("invalid hexadecimal data: odd number of digits")));
299
0
    if (!get_hex(s, &v2))
300
0
      ereturn(escontext, 0,
301
0
          (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
302
0
           errmsg("invalid hexadecimal digit: \"%.*s\"",
303
0
              pg_mblen_range(s, srcend), s)));
304
0
    s++;
305
0
    *p++ = (v1 << 4) | v2;
306
0
  }
307
308
0
  return p - dst;
309
0
}
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
hex_decode_simd_helper(const Vector8 src, Vector8 *dst)
320
0
{
321
0
  Vector8   sub;
322
0
  Vector8   mask_hi = vector8_interleave_low(vector8_broadcast(0), vector8_broadcast(0x0f));
323
0
  Vector8   mask_lo = vector8_interleave_low(vector8_broadcast(0x0f), vector8_broadcast(0));
324
0
  Vector8   tmp;
325
0
  bool    ret;
326
327
0
  tmp = vector8_gt(vector8_broadcast('9' + 1), src);
328
0
  sub = vector8_and(tmp, vector8_broadcast('0'));
329
330
0
  tmp = vector8_gt(src, vector8_broadcast('A' - 1));
331
0
  tmp = vector8_and(tmp, vector8_broadcast('A' - 10));
332
0
  sub = vector8_add(sub, tmp);
333
334
0
  tmp = vector8_gt(src, vector8_broadcast('a' - 1));
335
0
  tmp = vector8_and(tmp, vector8_broadcast('a' - 'A'));
336
0
  sub = vector8_add(sub, tmp);
337
338
0
  *dst = vector8_issub(src, sub);
339
0
  ret = !vector8_has_ge(*dst, 0x10);
340
341
0
  tmp = vector8_and(*dst, mask_hi);
342
0
  tmp = vector8_shift_right(tmp, 8);
343
0
  *dst = vector8_and(*dst, mask_lo);
344
0
  *dst = vector8_shift_left(*dst, 4);
345
0
  *dst = vector8_or(*dst, tmp);
346
0
  return ret;
347
0
}
348
#endif              /* ! USE_NO_SIMD */
349
350
uint64
351
hex_decode_safe(const char *src, size_t len, char *dst, Node *escontext)
352
0
{
353
#ifdef USE_NO_SIMD
354
  return hex_decode_safe_scalar(src, len, dst, escontext);
355
#else
356
0
  const uint64 tail_idx = len & ~(sizeof(Vector8) * 2 - 1);
357
0
  uint64    i;
358
0
  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
0
  for (i = 0; i < tail_idx; i += sizeof(Vector8) * 2)
365
0
  {
366
0
    Vector8   srcv;
367
0
    Vector8   dstv1;
368
0
    Vector8   dstv2;
369
370
0
    vector8_load(&srcv, (const uint8 *) &src[i]);
371
0
    success &= hex_decode_simd_helper(srcv, &dstv1);
372
373
0
    vector8_load(&srcv, (const uint8 *) &src[i + sizeof(Vector8)]);
374
0
    success &= hex_decode_simd_helper(srcv, &dstv2);
375
376
0
    vector8_store((uint8 *) &dst[i / 2], vector8_pack_16(dstv1, dstv2));
377
0
  }
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
0
  if (!success)
384
0
    i = 0;
385
386
0
  return i / 2 + hex_decode_safe_scalar(src + i, len - i, dst + i / 2, escontext);
387
0
#endif
388
0
}
389
390
static uint64
391
hex_enc_len(const char *src, size_t srclen)
392
0
{
393
0
  return (uint64) srclen << 1;
394
0
}
395
396
static uint64
397
hex_dec_len(const char *src, size_t srclen)
398
0
{
399
0
  return (uint64) srclen >> 1;
400
0
}
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
pg_base64_encode_internal(const char *src, size_t len, char *dst, bool url)
432
0
{
433
0
  char     *p,
434
0
         *lend = dst + 76;
435
0
  const char *s,
436
0
         *end = src + len;
437
0
  int     pos = 2;
438
0
  uint32    buf = 0;
439
0
  const char *alphabet = url ? _base64url : _base64;
440
441
0
  s = src;
442
0
  p = dst;
443
444
0
  while (s < end)
445
0
  {
446
0
    buf |= (unsigned char) *s << (pos << 3);
447
0
    pos--;
448
0
    s++;
449
450
    /* write it out */
451
0
    if (pos < 0)
452
0
    {
453
0
      *p++ = alphabet[(buf >> 18) & 0x3f];
454
0
      *p++ = alphabet[(buf >> 12) & 0x3f];
455
0
      *p++ = alphabet[(buf >> 6) & 0x3f];
456
0
      *p++ = alphabet[buf & 0x3f];
457
458
0
      pos = 2;
459
0
      buf = 0;
460
461
0
      if (!url && p >= lend)
462
0
      {
463
0
        *p++ = '\n';
464
0
        lend = p + 76;
465
0
      }
466
0
    }
467
0
  }
468
469
  /* Handle remaining bytes in buf */
470
0
  if (pos != 2)
471
0
  {
472
0
    *p++ = alphabet[(buf >> 18) & 0x3f];
473
0
    *p++ = alphabet[(buf >> 12) & 0x3f];
474
475
0
    if (pos == 0)
476
0
    {
477
0
      *p++ = alphabet[(buf >> 6) & 0x3f];
478
0
      if (!url)
479
0
        *p++ = '=';
480
0
    }
481
0
    else if (!url)
482
0
    {
483
0
      *p++ = '=';
484
0
      *p++ = '=';
485
0
    }
486
0
  }
487
488
0
  return p - dst;
489
0
}
490
491
static uint64
492
pg_base64_encode(const char *src, size_t len, char *dst)
493
0
{
494
0
  return pg_base64_encode_internal(src, len, dst, false);
495
0
}
496
497
static uint64
498
pg_base64url_encode(const char *src, size_t len, char *dst)
499
0
{
500
0
  return pg_base64_encode_internal(src, len, dst, true);
501
0
}
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
pg_base64_decode_internal(const char *src, size_t len, char *dst, bool url)
511
0
{
512
0
  const char *srcend = src + len,
513
0
         *s = src;
514
0
  char     *p = dst;
515
0
  char    c;
516
0
  int     b = 0;
517
0
  uint32    buf = 0;
518
0
  int     pos = 0,
519
0
        end = 0;
520
521
0
  while (s < srcend)
522
0
  {
523
0
    c = *s++;
524
525
0
    if (c == ' ' || c == '\t' || c == '\n' || c == '\r')
526
0
      continue;
527
528
    /* convert base64url to base64 */
529
0
    if (url)
530
0
    {
531
0
      if (c == '-')
532
0
        c = '+';
533
0
      else if (c == '_')
534
0
        c = '/';
535
0
    }
536
537
0
    if (c == '=')
538
0
    {
539
      /* end sequence */
540
0
      if (!end)
541
0
      {
542
0
        if (pos == 2)
543
0
          end = 1;
544
0
        else if (pos == 3)
545
0
          end = 2;
546
0
        else
547
0
        {
548
          /* translator: %s is the name of an encoding scheme */
549
0
          ereport(ERROR,
550
0
              (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
551
0
               errmsg("unexpected \"=\" while decoding %s sequence", url ? "base64url" : "base64")));
552
0
        }
553
0
      }
554
0
      b = 0;
555
0
    }
556
0
    else
557
0
    {
558
0
      b = -1;
559
0
      if (c > 0 && c < 127)
560
0
        b = b64lookup[(unsigned char) c];
561
0
      if (b < 0)
562
0
      {
563
        /* translator: %s is the name of an encoding scheme */
564
0
        ereport(ERROR,
565
0
            (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
566
0
             errmsg("invalid symbol \"%.*s\" found while decoding %s sequence",
567
0
                pg_mblen_range(s - 1, srcend), s - 1,
568
0
                url ? "base64url" : "base64")));
569
0
      }
570
0
    }
571
    /* add it to buffer */
572
0
    buf = (buf << 6) + b;
573
0
    pos++;
574
0
    if (pos == 4)
575
0
    {
576
0
      *p++ = (buf >> 16) & 255;
577
0
      if (end == 0 || end > 1)
578
0
        *p++ = (buf >> 8) & 255;
579
0
      if (end == 0 || end > 2)
580
0
        *p++ = buf & 255;
581
0
      buf = 0;
582
0
      pos = 0;
583
0
    }
584
0
  }
585
586
0
  if (url && pos == 2)
587
0
  {
588
0
    buf <<= 12;
589
0
    *p++ = (buf >> 16) & 0xFF;
590
0
  }
591
0
  else if (url && pos == 3)
592
0
  {
593
0
    buf <<= 6;
594
0
    *p++ = (buf >> 16) & 0xFF;
595
0
    *p++ = (buf >> 8) & 0xFF;
596
0
  }
597
0
  else if (pos != 0)
598
0
  {
599
    /* translator: %s is the name of an encoding scheme */
600
0
    ereport(ERROR,
601
0
        (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
602
0
         errmsg("invalid %s end sequence", url ? "base64url" : "base64"),
603
0
         errhint("Input data is missing padding, is truncated, or is otherwise corrupted.")));
604
0
  }
605
606
0
  return p - dst;
607
0
}
608
609
static uint64
610
pg_base64_decode(const char *src, size_t len, char *dst)
611
0
{
612
0
  return pg_base64_decode_internal(src, len, dst, false);
613
0
}
614
615
static uint64
616
pg_base64url_decode(const char *src, size_t len, char *dst)
617
0
{
618
0
  return pg_base64_decode_internal(src, len, dst, true);
619
0
}
620
621
static uint64
622
pg_base64_enc_len(const char *src, size_t srclen)
623
0
{
624
  /* 3 bytes will be converted to 4, linefeed after 76 chars */
625
0
  return ((uint64) srclen + 2) / 3 * 4 + (uint64) srclen / (76 * 3 / 4);
626
0
}
627
628
static uint64
629
pg_base64_dec_len(const char *src, size_t srclen)
630
0
{
631
0
  return ((uint64) srclen * 3) >> 2;
632
0
}
633
634
static uint64
635
pg_base64url_enc_len(const char *src, size_t srclen)
636
0
{
637
  /*
638
   * Unlike standard base64, base64url doesn't use padding characters when
639
   * the input length is not divisible by 3
640
   */
641
0
  return (srclen + 2) / 3 * 4;
642
0
}
643
644
static uint64
645
pg_base64url_dec_len(const char *src, size_t srclen)
646
0
{
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
0
  size_t    adjusted_len = srclen;
653
654
0
  if (srclen % 4 != 0)
655
0
    adjusted_len += 4 - (srclen % 4);
656
657
0
  return (adjusted_len * 3) / 4;
658
0
}
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
0
#define VAL(CH)     ((CH) - '0')
675
0
#define DIG(VAL)    ((VAL) + '0')
676
677
static uint64
678
esc_encode(const char *src, size_t srclen, char *dst)
679
0
{
680
0
  const char *end = src + srclen;
681
0
  char     *rp = dst;
682
0
  uint64    len = 0;
683
684
0
  while (src < end)
685
0
  {
686
0
    unsigned char c = (unsigned char) *src;
687
688
0
    if (c == '\0' || IS_HIGHBIT_SET(c))
689
0
    {
690
0
      rp[0] = '\\';
691
0
      rp[1] = DIG(c >> 6);
692
0
      rp[2] = DIG((c >> 3) & 7);
693
0
      rp[3] = DIG(c & 7);
694
0
      rp += 4;
695
0
      len += 4;
696
0
    }
697
0
    else if (c == '\\')
698
0
    {
699
0
      rp[0] = '\\';
700
0
      rp[1] = '\\';
701
0
      rp += 2;
702
0
      len += 2;
703
0
    }
704
0
    else
705
0
    {
706
0
      *rp++ = c;
707
0
      len++;
708
0
    }
709
710
0
    src++;
711
0
  }
712
713
0
  return len;
714
0
}
715
716
static uint64
717
esc_decode(const char *src, size_t srclen, char *dst)
718
0
{
719
0
  const char *end = src + srclen;
720
0
  char     *rp = dst;
721
0
  uint64    len = 0;
722
723
0
  while (src < end)
724
0
  {
725
0
    if (src[0] != '\\')
726
0
      *rp++ = *src++;
727
0
    else if (src + 3 < end &&
728
0
         (src[1] >= '0' && src[1] <= '3') &&
729
0
         (src[2] >= '0' && src[2] <= '7') &&
730
0
         (src[3] >= '0' && src[3] <= '7'))
731
0
    {
732
0
      int     val;
733
734
0
      val = VAL(src[1]);
735
0
      val <<= 3;
736
0
      val += VAL(src[2]);
737
0
      val <<= 3;
738
0
      *rp++ = val + VAL(src[3]);
739
0
      src += 4;
740
0
    }
741
0
    else if (src + 1 < end &&
742
0
         (src[1] == '\\'))
743
0
    {
744
0
      *rp++ = '\\';
745
0
      src += 2;
746
0
    }
747
0
    else
748
0
    {
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
0
          (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
755
0
           errmsg("invalid input syntax for type %s", "bytea")));
756
0
    }
757
758
0
    len++;
759
0
  }
760
761
0
  return len;
762
0
}
763
764
static uint64
765
esc_enc_len(const char *src, size_t srclen)
766
0
{
767
0
  const char *end = src + srclen;
768
0
  uint64    len = 0;
769
770
0
  while (src < end)
771
0
  {
772
0
    if (*src == '\0' || IS_HIGHBIT_SET(*src))
773
0
      len += 4;
774
0
    else if (*src == '\\')
775
0
      len += 2;
776
0
    else
777
0
      len++;
778
779
0
    src++;
780
0
  }
781
782
0
  return len;
783
0
}
784
785
static uint64
786
esc_dec_len(const char *src, size_t srclen)
787
0
{
788
0
  const char *end = src + srclen;
789
0
  uint64    len = 0;
790
791
0
  while (src < end)
792
0
  {
793
0
    if (src[0] != '\\')
794
0
      src++;
795
0
    else if (src + 3 < end &&
796
0
         (src[1] >= '0' && src[1] <= '3') &&
797
0
         (src[2] >= '0' && src[2] <= '7') &&
798
0
         (src[3] >= '0' && src[3] <= '7'))
799
0
    {
800
      /*
801
       * backslash + valid octal
802
       */
803
0
      src += 4;
804
0
    }
805
0
    else if (src + 1 < end &&
806
0
         (src[1] == '\\'))
807
0
    {
808
      /*
809
       * two backslashes = backslash
810
       */
811
0
      src += 2;
812
0
    }
813
0
    else
814
0
    {
815
      /*
816
       * one backslash, not followed by ### valid octal
817
       */
818
0
      ereport(ERROR,
819
0
          (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
820
0
           errmsg("invalid input syntax for type %s", "bytea")));
821
0
    }
822
823
0
    len++;
824
0
  }
825
0
  return len;
826
0
}
827
828
/*
829
 * BASE32HEX
830
 */
831
832
static const char base32hex_table[] = "0123456789ABCDEFGHIJKLMNOPQRSTUV";
833
834
static const int8 b32hexlookup[128] = {
835
  -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
836
  -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
837
  -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
838
  0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1,
839
  -1, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
840
  25, 26, 27, 28, 29, 30, 31, -1, -1, -1, -1, -1, -1, -1, -1, -1,
841
  -1, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
842
  25, 26, 27, 28, 29, 30, 31, -1, -1, -1, -1, -1, -1, -1, -1, -1,
843
};
844
845
static uint64
846
base32hex_enc_len(const char *src, size_t srclen)
847
0
{
848
  /* 5 bytes encode to 8 characters, round up to multiple of 8 for padding */
849
0
  return ((uint64) srclen + 4) / 5 * 8;
850
0
}
851
852
static uint64
853
base32hex_dec_len(const char *src, size_t srclen)
854
0
{
855
  /* Each 8 characters of input produces at most 5 bytes of output */
856
0
  return ((uint64) srclen * 5) / 8;
857
0
}
858
859
static uint64
860
base32hex_encode(const char *src, size_t srclen, char *dst)
861
0
{
862
0
  const unsigned char *data = (const unsigned char *) src;
863
0
  uint32    bits_buffer = 0;
864
0
  int     bits_in_buffer = 0;
865
0
  uint64    output_pos = 0;
866
0
  size_t    i;
867
868
0
  for (i = 0; i < srclen; i++)
869
0
  {
870
    /* Add 8 bits to the buffer */
871
0
    bits_buffer = (bits_buffer << 8) | data[i];
872
0
    bits_in_buffer += 8;
873
874
    /* Extract 5-bit chunks while we have enough bits */
875
0
    while (bits_in_buffer >= 5)
876
0
    {
877
0
      bits_in_buffer -= 5;
878
      /* Extract top 5 bits */
879
0
      dst[output_pos++] = base32hex_table[(bits_buffer >> bits_in_buffer) & 0x1F];
880
      /* Clear the extracted bits by masking */
881
0
      bits_buffer &= ((1U << bits_in_buffer) - 1);
882
0
    }
883
0
  }
884
885
  /* Handle remaining bits (if any) */
886
0
  if (bits_in_buffer > 0)
887
0
    dst[output_pos++] = base32hex_table[(bits_buffer << (5 - bits_in_buffer)) & 0x1F];
888
889
  /* Add padding to make length a multiple of 8 (per RFC 4648) */
890
0
  while (output_pos % 8 != 0)
891
0
    dst[output_pos++] = '=';
892
893
0
  return output_pos;
894
0
}
895
896
static uint64
897
base32hex_decode(const char *src, size_t srclen, char *dst)
898
0
{
899
0
  const char *srcend = src + srclen,
900
0
         *s = src;
901
0
  uint32    bits_buffer = 0;
902
0
  int     bits_in_buffer = 0;
903
0
  uint64    output_pos = 0;
904
0
  int     pos = 0;    /* position within 8-character group (0-7) */
905
0
  bool    end = false;  /* have we seen padding? */
906
907
0
  while (s < srcend)
908
0
  {
909
0
    char    c = *s++;
910
0
    int     val;
911
912
    /* Skip whitespace */
913
0
    if (c == ' ' || c == '\t' || c == '\n' || c == '\r')
914
0
      continue;
915
916
0
    if (c == '=')
917
0
    {
918
      /*
919
       * The first padding is only valid at positions 2, 4, 5, or 7
920
       * within an 8-character group (corresponding to 1, 2, 3, or 4
921
       * input bytes). We only check the position for the first '='
922
       * character.
923
       */
924
0
      if (!end)
925
0
      {
926
0
        if (pos != 2 && pos != 4 && pos != 5 && pos != 7)
927
0
          ereport(ERROR,
928
0
              (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
929
0
               errmsg("unexpected \"=\" while decoding base32hex sequence")));
930
0
        end = true;
931
0
      }
932
0
      pos++;
933
0
      continue;
934
0
    }
935
936
    /* No data characters allowed after padding */
937
0
    if (end)
938
0
      ereport(ERROR,
939
0
          (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
940
0
           errmsg("invalid symbol \"%.*s\" found while decoding base32hex sequence",
941
0
              pg_mblen_range(s - 1, srcend), s - 1)));
942
943
    /* Decode base32hex character (0-9, A-V, case-insensitive) */
944
0
    val = -1;
945
0
    if ((unsigned char) c < 128)
946
0
      val = b32hexlookup[(unsigned char) c];
947
0
    if (val < 0)
948
0
      ereport(ERROR,
949
0
          (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
950
0
           errmsg("invalid symbol \"%.*s\" found while decoding base32hex sequence",
951
0
              pg_mblen_range(s - 1, srcend), s - 1)));
952
953
    /* Add 5 bits to buffer */
954
0
    bits_buffer = (bits_buffer << 5) | val;
955
0
    bits_in_buffer += 5;
956
0
    pos++;
957
958
    /* Extract 8-bit bytes when we have enough bits */
959
0
    while (bits_in_buffer >= 8)
960
0
    {
961
0
      bits_in_buffer -= 8;
962
0
      dst[output_pos++] = (unsigned char) (bits_buffer >> bits_in_buffer);
963
      /* Clear the extracted bits */
964
0
      bits_buffer &= ((1U << bits_in_buffer) - 1);
965
0
    }
966
967
    /* Reset position after each complete 8-character group */
968
0
    if (pos == 8)
969
0
      pos = 0;
970
0
  }
971
972
0
  return output_pos;
973
0
}
974
975
/*
976
 * Common
977
 */
978
979
static const struct
980
{
981
  const char *name;
982
  struct pg_encoding enc;
983
}     enclist[] =
984
985
{
986
  {
987
    "hex",
988
    {
989
      hex_enc_len, hex_dec_len, hex_encode, hex_decode
990
    }
991
  },
992
  {
993
    "base64",
994
    {
995
      pg_base64_enc_len, pg_base64_dec_len, pg_base64_encode, pg_base64_decode
996
    }
997
  },
998
  {
999
    "base64url",
1000
    {
1001
      pg_base64url_enc_len, pg_base64url_dec_len, pg_base64url_encode, pg_base64url_decode
1002
    }
1003
  },
1004
  {
1005
    "base32hex",
1006
    {
1007
      base32hex_enc_len, base32hex_dec_len, base32hex_encode, base32hex_decode
1008
    }
1009
  },
1010
  {
1011
    "escape",
1012
    {
1013
      esc_enc_len, esc_dec_len, esc_encode, esc_decode
1014
    }
1015
  },
1016
  {
1017
    NULL,
1018
    {
1019
      NULL, NULL, NULL, NULL
1020
    }
1021
  }
1022
};
1023
1024
static const struct pg_encoding *
1025
pg_find_encoding(const char *name)
1026
0
{
1027
0
  int     i;
1028
1029
0
  for (i = 0; enclist[i].name; i++)
1030
0
    if (pg_strcasecmp(enclist[i].name, name) == 0)
1031
0
      return &enclist[i].enc;
1032
1033
0
  return NULL;
1034
0
}