escape: polish encoding and decoding for speed
No binary (build failed)
—
(log yok)
| @@ -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; |
| } | |
| /* | |
| @@ -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> | |
| @@ -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 }, | |