static byte parseHexByte(const char * &str) {
    byte b = parseHexChar(str[0]);
    if (str[1] == ':' || str[1] == '\0') {
        str += 2;
        return b;
    } else {
        b = b << 4 | parseHexChar(str[1]);
        str += 3;
        return b;
    }
}
int AtMegaWebServer::unescapeChars(char* str){
	int ret = 0;
	while(*str && *str != '%') str++;
	if(*str){
		char* dest = str;
		do{
			if(parseHexChar(*(str + 1)) >= 0 && parseHexChar(*(str + 2)) >= 0){
				*dest = (parseHexChar(*(str + 1)) << 4) + parseHexChar(*(str + 2));
				str += 3;
				dest++;
				ret += 2;
			}
			while(*str && *str != '%'){
				*dest = *str;
				dest++; str++;
			}
		}while(*str);
		*dest = 0;
	}
	return ret;
}
char* MiniWebServer::decode_url_encoded(const char* s) {
    if (!s) {
        return NULL;
    }
    char* r = (char*)malloc_check(strlen(s) + 1);
    if (!r) {
        return NULL;
    }
    char* r2 = r;
    const char* p = s;
    while (*s && (p = strchr(s, '%'))) {
        if (p - s) {
            memcpy(r2, s, p - s);
            r2 += (p - s);
        }
        // If the remaining number of characters is less than 3, we cannot
        // have a complete escape sequence. Break early.
        if (strlen(p) < 3) {
            // Move the new beginning to the value of p.
            s = p;
            break;
        }
        uint8_t r = parseHexChar(*(p + 1)) << 4 | parseHexChar(*(p + 2));
        *r2++ = r;
        p += 3;

        // Move the new beginning to the value of p.
        s = p;
    }
    // Copy whatever is left of the string in the result.
    int len = strlen(s);
    if (len > 0) {
        strncpy(r2, s, len);
    }
    // Append the 0 terminator.
    *(r2 + len) = 0;

    return r;
}
byte parseHexByte(char ch1, char ch2) {
    return (parseHexChar(ch1) << 4) | parseHexChar(ch2);
}