mirror of https://github.com/postgres/postgres
This is a combined revert of the following commits: -pull/68/headc3826f8
, a refactoring piece that moved the hex decoding code to src/common/. This code was cleaned up byaef8948
, as it originally included no overflow checks in the same way as the base64 routines in src/common/ used by SCRAM, making it unsafe for its purpose. -aef8948
, a more advanced refactoring of the hex encoding/decoding code to src/common/ that added sanity checks on the result buffer for hex decoding and encoding. As reported by Hans Buschmann, those overflow checks are expensive, and it is possible to see a performance drop in the decoding/encoding of bytea or LOs the longer they are. Simple SQLs working on large bytea values show a clear difference in perf profile. -ccf4e27
, a cleanup made possible byaef8948
. The reverts of all those commits bring back the performance of hex decoding and encoding back to what it was in ~13. Fow now and post-beta3, this is the simplest option. Reported-by: Hans Buschmann Discussion: https://postgr.es/m/1629039545467.80333@nidsa.net Backpatch-through: 14
parent
2313dda9d4
commit
2576dcfb76
@ -1,192 +0,0 @@ |
||||
/*-------------------------------------------------------------------------
|
||||
* |
||||
* hex.c |
||||
* Encoding and decoding routines for hex. |
||||
* |
||||
* Portions Copyright (c) 1996-2021, PostgreSQL Global Development Group |
||||
* Portions Copyright (c) 1994, Regents of the University of California |
||||
* |
||||
* IDENTIFICATION |
||||
* src/common/hex.c |
||||
* |
||||
*------------------------------------------------------------------------- |
||||
*/ |
||||
|
||||
|
||||
#ifndef FRONTEND |
||||
#include "postgres.h" |
||||
#else |
||||
#include "postgres_fe.h" |
||||
#endif |
||||
|
||||
#include "common/hex.h" |
||||
#ifdef FRONTEND |
||||
#include "common/logging.h" |
||||
#endif |
||||
#include "mb/pg_wchar.h" |
||||
|
||||
|
||||
static const int8 hexlookup[128] = { |
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, |
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, |
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, |
||||
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1, |
||||
-1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, |
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, |
||||
-1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, |
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, |
||||
}; |
||||
|
||||
static const char hextbl[] = "0123456789abcdef"; |
||||
|
||||
static inline char |
||||
get_hex(const char *cp) |
||||
{ |
||||
unsigned char c = (unsigned char) *cp; |
||||
int res = -1; |
||||
|
||||
if (c < 127) |
||||
res = hexlookup[c]; |
||||
|
||||
if (res < 0) |
||||
{ |
||||
#ifdef FRONTEND |
||||
pg_log_fatal("invalid hexadecimal digit"); |
||||
exit(EXIT_FAILURE); |
||||
#else |
||||
ereport(ERROR, |
||||
(errcode(ERRCODE_INVALID_PARAMETER_VALUE), |
||||
errmsg("invalid hexadecimal digit: \"%.*s\"", |
||||
pg_mblen(cp), cp))); |
||||
#endif |
||||
} |
||||
|
||||
return (char) res; |
||||
} |
||||
|
||||
/*
|
||||
* pg_hex_encode |
||||
* |
||||
* Encode into hex the given string. Returns the length of the encoded |
||||
* string. |
||||
*/ |
||||
uint64 |
||||
pg_hex_encode(const char *src, size_t srclen, char *dst, size_t dstlen) |
||||
{ |
||||
const char *end = src + srclen; |
||||
char *p; |
||||
|
||||
p = dst; |
||||
|
||||
while (src < end) |
||||
{ |
||||
/*
|
||||
* Leave if there is an overflow in the area allocated for the encoded |
||||
* string. |
||||
*/ |
||||
if ((p - dst + 2) > dstlen) |
||||
{ |
||||
#ifdef FRONTEND |
||||
pg_log_fatal("overflow of destination buffer in hex encoding"); |
||||
exit(EXIT_FAILURE); |
||||
#else |
||||
elog(ERROR, "overflow of destination buffer in hex encoding"); |
||||
#endif |
||||
} |
||||
|
||||
*p++ = hextbl[(*src >> 4) & 0xF]; |
||||
*p++ = hextbl[*src & 0xF]; |
||||
src++; |
||||
} |
||||
|
||||
Assert((p - dst) <= dstlen); |
||||
return p - dst; |
||||
} |
||||
|
||||
/*
|
||||
* pg_hex_decode |
||||
* |
||||
* Decode the given hex string. Returns the length of the decoded string. |
||||
*/ |
||||
uint64 |
||||
pg_hex_decode(const char *src, size_t srclen, char *dst, size_t dstlen) |
||||
{ |
||||
const char *s, |
||||
*srcend; |
||||
char v1, |
||||
v2, |
||||
*p; |
||||
|
||||
srcend = src + srclen; |
||||
s = src; |
||||
p = dst; |
||||
while (s < srcend) |
||||
{ |
||||
if (*s == ' ' || *s == '\n' || *s == '\t' || *s == '\r') |
||||
{ |
||||
s++; |
||||
continue; |
||||
} |
||||
v1 = get_hex(s) << 4; |
||||
s++; |
||||
|
||||
if (s >= srcend) |
||||
{ |
||||
#ifdef FRONTEND |
||||
pg_log_fatal("invalid hexadecimal data: odd number of digits"); |
||||
exit(EXIT_FAILURE); |
||||
#else |
||||
ereport(ERROR, |
||||
(errcode(ERRCODE_INVALID_PARAMETER_VALUE), |
||||
errmsg("invalid hexadecimal data: odd number of digits"))); |
||||
#endif |
||||
} |
||||
|
||||
v2 = get_hex(s); |
||||
s++; |
||||
|
||||
/* overflow check */ |
||||
if ((p - dst + 1) > dstlen) |
||||
{ |
||||
#ifdef FRONTEND |
||||
pg_log_fatal("overflow of destination buffer in hex decoding"); |
||||
exit(EXIT_FAILURE); |
||||
#else |
||||
elog(ERROR, "overflow of destination buffer in hex decoding"); |
||||
#endif |
||||
} |
||||
|
||||
*p++ = v1 | v2; |
||||
} |
||||
|
||||
Assert((p - dst) <= dstlen); |
||||
return p - dst; |
||||
} |
||||
|
||||
/*
|
||||
* pg_hex_enc_len |
||||
* |
||||
* Returns to caller the length of the string if it were encoded with |
||||
* hex based on the length provided by caller. This is useful to estimate |
||||
* how large a buffer allocation needs to be done before doing the actual |
||||
* encoding. |
||||
*/ |
||||
uint64 |
||||
pg_hex_enc_len(size_t srclen) |
||||
{ |
||||
return (uint64) srclen << 1; |
||||
} |
||||
|
||||
/*
|
||||
* pg_hex_dec_len |
||||
* |
||||
* Returns to caller the length of the string if it were to be decoded |
||||
* with hex, based on the length given by caller. This is useful to |
||||
* estimate how large a buffer allocation needs to be done before doing |
||||
* the actual decoding. |
||||
*/ |
||||
uint64 |
||||
pg_hex_dec_len(size_t srclen) |
||||
{ |
||||
return (uint64) srclen >> 1; |
||||
} |
@ -1,25 +0,0 @@ |
||||
/*------------------------------------------------------------------------
|
||||
* |
||||
* hex.h |
||||
* Encoding and decoding routines for hex strings. |
||||
* |
||||
* Portions Copyright (c) 1996-2021, PostgreSQL Global Development Group |
||||
* Portions Copyright (c) 1994, Regents of the University of California |
||||
* |
||||
* IDENTIFICATION |
||||
* src/include/common/hex.h |
||||
* |
||||
*------------------------------------------------------------------------ |
||||
*/ |
||||
|
||||
#ifndef COMMON_HEX_H |
||||
#define COMMON_HEX_H |
||||
|
||||
extern uint64 pg_hex_decode(const char *src, size_t srclen, |
||||
char *dst, size_t dstlen); |
||||
extern uint64 pg_hex_encode(const char *src, size_t srclen, |
||||
char *dst, size_t dstlen); |
||||
extern uint64 pg_hex_enc_len(size_t srclen); |
||||
extern uint64 pg_hex_dec_len(size_t srclen); |
||||
|
||||
#endif /* COMMON_HEX_H */ |
Loading…
Reference in new issue