← Commit list

48e196426b

Daniel Stenberg
Daniel Stenberg
2026-09-03
SUCCESS Duration: 402s

Subject

escape: polish encoding and decoding for speed

Binary

No binary (build failed)

Architecture & Compiler Flags

Architecture: x86_64-linux-gnu Compiler: gcc (Ubuntu 11.4.0-1ubuntu1~22.04.3) 11.4.0

—

Build Log

(log yok)

Commit Diff GitHub →

lib/escape.c+113 -30
@@ -44,13 +44,55 @@ char *curl_unescape(const char *string, int length)
return curl_easy_unescape(NULL, string, length, NULL);
}
+#define NOPE 0xff
+#define _OK_ 0x1f /* octet not needing encoding but not a hexadecimal
+ character */
+
+static const unsigned char hextable[256] = {
+ NOPE, NOPE, NOPE, NOPE, NOPE, NOPE, NOPE, NOPE, /* 00 */
+ NOPE, NOPE, NOPE, NOPE, NOPE, NOPE, NOPE, NOPE, /* 08 */
+ NOPE, NOPE, NOPE, NOPE, NOPE, NOPE, NOPE, NOPE, /* 10 */
+ NOPE, NOPE, NOPE, NOPE, NOPE, NOPE, NOPE, NOPE, /* 18 */
+ NOPE, NOPE, NOPE, NOPE, NOPE, NOPE, NOPE, NOPE, /* 20 */
+ NOPE, NOPE, NOPE, NOPE, NOPE, _OK_, _OK_, NOPE, /* 28 */
+ 0, 1, 2, 3, 4, 5, 6, 7, /* 30 */
+ 8, 9, NOPE, NOPE, NOPE, NOPE, NOPE, NOPE, /* 38 */
+ NOPE, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, _OK_, /* 40 */
+ _OK_, _OK_, _OK_, _OK_, _OK_, _OK_, _OK_, _OK_, /* 48 */
+ _OK_, _OK_, _OK_, _OK_, _OK_, _OK_, _OK_, _OK_, /* 50 */
+ _OK_, _OK_, _OK_, NOPE, NOPE, NOPE, NOPE, _OK_, /* 58 */
+ NOPE, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, _OK_, /* 60 */
+ _OK_, _OK_, _OK_, _OK_, _OK_, _OK_, _OK_, _OK_, /* 68 */
+ _OK_, _OK_, _OK_, _OK_, _OK_, _OK_, _OK_, _OK_, /* 70 */
+ _OK_, _OK_, _OK_, NOPE, NOPE, NOPE, _OK_, NOPE, /* 78 */
+ NOPE, NOPE, NOPE, NOPE, NOPE, NOPE, NOPE, NOPE, /* 80 - not ASCII */
+ NOPE, NOPE, NOPE, NOPE, NOPE, NOPE, NOPE, NOPE,
+ NOPE, NOPE, NOPE, NOPE, NOPE, NOPE, NOPE, NOPE,
+ NOPE, NOPE, NOPE, NOPE, NOPE, NOPE, NOPE, NOPE,
+ NOPE, NOPE, NOPE, NOPE, NOPE, NOPE, NOPE, NOPE,
+ NOPE, NOPE, NOPE, NOPE, NOPE, NOPE, NOPE, NOPE,
+ NOPE, NOPE, NOPE, NOPE, NOPE, NOPE, NOPE, NOPE,
+ NOPE, NOPE, NOPE, NOPE, NOPE, NOPE, NOPE, NOPE,
+ NOPE, NOPE, NOPE, NOPE, NOPE, NOPE, NOPE, NOPE,
+ NOPE, NOPE, NOPE, NOPE, NOPE, NOPE, NOPE, NOPE,
+ NOPE, NOPE, NOPE, NOPE, NOPE, NOPE, NOPE, NOPE,
+ NOPE, NOPE, NOPE, NOPE, NOPE, NOPE, NOPE, NOPE,
+ NOPE, NOPE, NOPE, NOPE, NOPE, NOPE, NOPE, NOPE,
+ NOPE, NOPE, NOPE, NOPE, NOPE, NOPE, NOPE, NOPE,
+ NOPE, NOPE, NOPE, NOPE, NOPE, NOPE, NOPE, NOPE,
+ NOPE, NOPE, NOPE, NOPE, NOPE, NOPE, NOPE, NOPE
+};
+
/* Escapes for URL the given unescaped string of given length.
* 'data' is ignored since 7.82.0.
*/
char *curl_easy_escape(CURL *curl, const char *string, int length)
{
size_t len;
- struct dynbuf d;
+ const char *f = string;
+ size_t nappend = 0;
+ char *target;
+ char *encoded;
(void)curl;
if(!string || (length < 0))
@@ -63,27 +105,37 @@ char *curl_easy_escape(CURL *curl, const char *string, int length)
if(len > SIZE_MAX / 16)
return NULL;
- curlx_dyn_init(&d, (len * 3) + 1);
+ encoded = target = curlx_malloc((len * 3) + 1);
+ if(!target)
+ return NULL;
while(len--) {
- /* treat the characters unsigned */
- unsigned char in = (unsigned char)*string++;
+ uint8_t in = (uint8_t)*string++;
- if(ISUNRESERVED(in)) {
+ if(!(hextable[in] & 0x80))
/* append this */
- if(curlx_dyn_addn(&d, &in, 1))
- return NULL;
- }
+ nappend++;
else {
+ if(nappend) {
+ memcpy(target, f, nappend);
+ target += nappend;
+ nappend = 0;
+ }
+ f = string;
/* encode it */
- unsigned char out[3] = { '%' };
- Curl_hexbyte(&out[1], in);
- if(curlx_dyn_addn(&d, out, 3))
- return NULL;
+ target[0] = '%';
+ target[1] = Curl_udigits[in >> 4];
+ target[2] = Curl_udigits[in & 0x0F];
+ target += 3;
}
}
+ if(nappend) {
+ memcpy(target, f, nappend);
+ target += nappend;
+ }
+ *target = '\0'; /* null terminate */
- return curlx_dyn_ptr(&d);
+ return encoded;
}
/*
@@ -108,6 +160,7 @@ CURLcode Curl_urldecode(const char *string, size_t length,
{
size_t alloc;
char *ns;
+ uint8_t reject_limit;
DEBUGASSERT(string);
DEBUGASSERT(ctrl >= REJECT_NADA); /* crash on TRUE/FALSE */
@@ -121,36 +174,66 @@ CURLcode Curl_urldecode(const char *string, size_t length,
/* store output string */
*ostring = ns;
+ reject_limit = (ctrl == REJECT_CTRL) ? 0x20 :
+ (ctrl == REJECT_ZERO) ? 1 : 0;
+
while(alloc) {
- unsigned char in = (unsigned char)*string;
- if(('%' == in) && (alloc > 2) &&
- ISXDIGIT(string[1]) && ISXDIGIT(string[2])) {
- /* this is two hexadecimal digits following a '%' */
- in = (unsigned char)((curlx_hexval(string[1]) << 4) |
- curlx_hexval(string[2]));
- string += 3;
- alloc -= 3;
- }
- else {
+ uint8_t in;
+
+ if(*string == '%') {
+ if(alloc > 2) {
+ uint8_t h1 = hextable[(uint8_t)string[1]];
+ uint8_t h2 = hextable[(uint8_t)string[2]];
+ if(!((h1 | h2) & 0xf0)) {
+ in = (uint8_t)((h1 << 4) | h2);
+ string += 3;
+ alloc -= 3;
+ if(in < reject_limit)
+ goto error;
+ *ns++ = (char)in;
+ continue;
+ }
+ }
+ in = '%';
string++;
alloc--;
+ *ns++ = (char)in;
+ continue;
}
+ else {
+ const char *p = memchr(string + 1, '%', alloc - 1);
+ size_t n = p ? (size_t)(p - string) : alloc;
- if(((ctrl == REJECT_CTRL) && (in < 0x20)) ||
- ((ctrl == REJECT_ZERO) && (in == 0))) {
- curlx_safefree(*ostring);
- return CURLE_URL_MALFORMAT;
- }
+ if(reject_limit) {
+ if(reject_limit == 1) {
+ if(memchr(string, 0, n))
+ goto error;
+ }
+ else {
+ size_t i;
+ for(i = 0; i < n; i++) {
+ if((uint8_t)string[i] < 0x20)
+ goto error;
+ }
+ }
+ }
- *ns++ = (char)in;
+ memcpy(ns, string, n);
+ ns += n;
+ string += n;
+ alloc -= n;
+ }
}
*ns = 0; /* terminate it */
if(olen)
/* store output size */
- *olen = ns - *ostring;
+ *olen = (size_t)(ns - *ostring);
return CURLE_OK;
+error:
+ curlx_safefree(*ostring);
+ return CURLE_URL_MALFORMAT;
}
/*
tests/data/test558+1 -2
@@ -32,8 +32,7 @@ libtest memory tracking operational
MEM easy.c: malloc()
MEM lib%TESTNUMBER.c: malloc()
MEM lib%TESTNUMBER.c: free()
-MEM dynbuf.c: realloc()
-MEM dynbuf.c: realloc()
+MEM escape.c: malloc()
MEM escape.c: free()
MEM easy.c: free()
</file>
tests/libtest/lib1396.c+4 -0
@@ -69,6 +69,10 @@ static CURLcode test_lib1396(const char *arg)
/* escape, this => that */
static const struct test list2[] = {
{ "a", 1, "a", 1 },
+ { "-", 1, "-", 1 },
+ { ".", 1, ".", 1 },
+ { "_", 1, "_", 1 },
+ { "~", 1, "~", 1 },
{ "/", 1, "%2F", 3 },
{ "a=b", 3, "a%3Db", 5 },
{ "a=b", 0, "a%3Db", 5 },