blob: 22ae36a90ac6bf1780e2aa6444f5c17798bc76a6 [file] [log] [blame]
/*
This file is part of libmicrohttpd
Copyright (C) 2015-2022 Karlson2k (Evgeny Grin)
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
/**
* @file microhttpd/mhd_str.c
* @brief Functions implementations for string manipulating
* @author Karlson2k (Evgeny Grin)
*/
#include "mhd_str.h"
#ifdef HAVE_STDBOOL_H
#include <stdbool.h>
#endif /* HAVE_STDBOOL_H */
#include <string.h>
#include "mhd_assert.h"
#include "mhd_limits.h"
#include "mhd_assert.h"
#ifdef MHD_FAVOR_SMALL_CODE
#ifdef _MHD_static_inline
#undef _MHD_static_inline
#endif /* _MHD_static_inline */
/* Do not force inlining and do not use macro functions, use normal static
functions instead.
This may give more flexibility for size optimizations. */
#define _MHD_static_inline static
#ifndef INLINE_FUNC
#define INLINE_FUNC 1
#endif /* !INLINE_FUNC */
#endif /* MHD_FAVOR_SMALL_CODE */
/*
* Block of functions/macros that use US-ASCII charset as required by HTTP
* standards. Not affected by current locale settings.
*/
#ifdef INLINE_FUNC
#if 0 /* Disable unused functions. */
/**
* Check whether character is lower case letter in US-ASCII
*
* @param c character to check
* @return non-zero if character is lower case letter, zero otherwise
*/
_MHD_static_inline bool
isasciilower (char c)
{
return (c >= 'a') && (c <= 'z');
}
#endif /* Disable unused functions. */
/**
* Check whether character is upper case letter in US-ASCII
*
* @param c character to check
* @return non-zero if character is upper case letter, zero otherwise
*/
_MHD_static_inline bool
isasciiupper (char c)
{
return (c >= 'A') && (c <= 'Z');
}
#if 0 /* Disable unused functions. */
/**
* Check whether character is letter in US-ASCII
*
* @param c character to check
* @return non-zero if character is letter in US-ASCII, zero otherwise
*/
_MHD_static_inline bool
isasciialpha (char c)
{
return isasciilower (c) || isasciiupper (c);
}
#endif /* Disable unused functions. */
/**
* Check whether character is decimal digit in US-ASCII
*
* @param c character to check
* @return non-zero if character is decimal digit, zero otherwise
*/
_MHD_static_inline bool
isasciidigit (char c)
{
return (c >= '0') && (c <= '9');
}
#if 0 /* Disable unused functions. */
/**
* Check whether character is hexadecimal digit in US-ASCII
*
* @param c character to check
* @return non-zero if character is decimal digit, zero otherwise
*/
_MHD_static_inline bool
isasciixdigit (char c)
{
return isasciidigit (c) ||
( (c >= 'A') && (c <= 'F') ) ||
( (c >= 'a') && (c <= 'f') );
}
/**
* Check whether character is decimal digit or letter in US-ASCII
*
* @param c character to check
* @return non-zero if character is decimal digit or letter, zero otherwise
*/
_MHD_static_inline bool
isasciialnum (char c)
{
return isasciialpha (c) || isasciidigit (c);
}
#endif /* Disable unused functions. */
#if 0 /* Disable unused functions. */
/**
* Convert US-ASCII character to lower case.
* If character is upper case letter in US-ASCII than it's converted to lower
* case analog. If character is NOT upper case letter than it's returned
* unmodified.
*
* @param c character to convert
* @return converted to lower case character
*/
_MHD_static_inline char
toasciilower (char c)
{
return isasciiupper (c) ? (c - 'A' + 'a') : c;
}
/**
* Convert US-ASCII character to upper case.
* If character is lower case letter in US-ASCII than it's converted to upper
* case analog. If character is NOT lower case letter than it's returned
* unmodified.
*
* @param c character to convert
* @return converted to upper case character
*/
_MHD_static_inline char
toasciiupper (char c)
{
return isasciilower (c) ? (c - 'a' + 'A') : c;
}
#endif /* Disable unused functions. */
#if defined(MHD_FAVOR_SMALL_CODE) /* Used only in MHD_str_to_uvalue_n_() */
/**
* Convert US-ASCII decimal digit to its value.
*
* @param c character to convert
* @return value of decimal digit or -1 if @ c is not decimal digit
*/
_MHD_static_inline int
todigitvalue (char c)
{
if (isasciidigit (c))
return (unsigned char) (c - '0');
return -1;
}
#endif /* MHD_FAVOR_SMALL_CODE */
/**
* Convert US-ASCII hexadecimal digit to its value.
*
* @param c character to convert
* @return value of hexadecimal digit or -1 if @ c is not hexadecimal digit
*/
_MHD_static_inline int
toxdigitvalue (char c)
{
if (isasciidigit (c))
return (unsigned char) (c - '0');
if ( (c >= 'A') && (c <= 'F') )
return (unsigned char) (c - 'A' + 10);
if ( (c >= 'a') && (c <= 'f') )
return (unsigned char) (c - 'a' + 10);
return -1;
}
/**
* Caseless compare two characters.
*
* @param c1 the first char to compare
* @param c2 the second char to compare
* @return boolean 'true' if chars are caseless equal, false otherwise
*/
_MHD_static_inline bool
charsequalcaseless (const char c1, const char c2)
{
return ( (c1 == c2) ||
(isasciiupper (c1) ?
((c1 - 'A' + 'a') == c2) :
((c1 == (c2 - 'A' + 'a')) && isasciiupper (c2))) );
}
#else /* !INLINE_FUNC */
/**
* Checks whether character is lower case letter in US-ASCII
*
* @param c character to check
* @return boolean true if character is lower case letter,
* boolean false otherwise
*/
#define isasciilower(c) (((char) (c)) >= 'a' && ((char) (c)) <= 'z')
/**
* Checks whether character is upper case letter in US-ASCII
*
* @param c character to check
* @return boolean true if character is upper case letter,
* boolean false otherwise
*/
#define isasciiupper(c) (((char) (c)) >= 'A' && ((char) (c)) <= 'Z')
/**
* Checks whether character is letter in US-ASCII
*
* @param c character to check
* @return boolean true if character is letter, boolean false
* otherwise
*/
#define isasciialpha(c) (isasciilower (c) || isasciiupper (c))
/**
* Check whether character is decimal digit in US-ASCII
*
* @param c character to check
* @return boolean true if character is decimal digit, boolean false
* otherwise
*/
#define isasciidigit(c) (((char) (c)) >= '0' && ((char) (c)) <= '9')
/**
* Check whether character is hexadecimal digit in US-ASCII
*
* @param c character to check
* @return boolean true if character is hexadecimal digit,
* boolean false otherwise
*/
#define isasciixdigit(c) (isasciidigit ((c)) || \
(((char) (c)) >= 'A' && ((char) (c)) <= 'F') || \
(((char) (c)) >= 'a' && ((char) (c)) <= 'f') )
/**
* Check whether character is decimal digit or letter in US-ASCII
*
* @param c character to check
* @return boolean true if character is decimal digit or letter,
* boolean false otherwise
*/
#define isasciialnum(c) (isasciialpha (c) || isasciidigit (c))
/**
* Convert US-ASCII character to lower case.
* If character is upper case letter in US-ASCII than it's converted to lower
* case analog. If character is NOT upper case letter than it's returned
* unmodified.
*
* @param c character to convert
* @return converted to lower case character
*/
#define toasciilower(c) ((isasciiupper (c)) ? (((char) (c)) - 'A' + 'a') : \
((char) (c)))
/**
* Convert US-ASCII character to upper case.
* If character is lower case letter in US-ASCII than it's converted to upper
* case analog. If character is NOT lower case letter than it's returned
* unmodified.
*
* @param c character to convert
* @return converted to upper case character
*/
#define toasciiupper(c) ((isasciilower (c)) ? (((char) (c)) - 'a' + 'A') : \
((char) (c)))
/**
* Convert US-ASCII decimal digit to its value.
*
* @param c character to convert
* @return value of hexadecimal digit or -1 if @ c is not hexadecimal digit
*/
#define todigitvalue(c) (isasciidigit (c) ? (int) (((char) (c)) - '0') : \
(int) (-1))
/**
* Convert US-ASCII hexadecimal digit to its value.
* @param c character to convert
* @return value of hexadecimal digit or -1 if @ c is not hexadecimal digit
*/
#define toxdigitvalue(c) (isasciidigit (c) ? (int) (((char) (c)) - '0') : \
( (((char) (c)) >= 'A' && ((char) (c)) <= 'F') ? \
(int) (((unsigned char) (c)) - 'A' + 10) : \
( (((char) (c)) >= 'a' && ((char) (c)) <= 'f') ? \
(int) (((unsigned char) (c)) - 'a' + 10) : \
(int) (-1) )))
/**
* Caseless compare two characters.
*
* @param c1 the first char to compare
* @param c2 the second char to compare
* @return boolean 'true' if chars are caseless equal, false otherwise
*/
#define charsequalcaseless(c1, c2) \
( ((c1) == (c2)) || \
(isasciiupper (c1) ? \
(((c1) - 'A' + 'a') == (c2)) : \
(((c1) == ((c2) - 'A' + 'a')) && isasciiupper (c2))) )
#endif /* !INLINE_FUNC */
#ifndef MHD_FAVOR_SMALL_CODE
/**
* Check two strings for equality, ignoring case of US-ASCII letters.
*
* @param str1 first string to compare
* @param str2 second string to compare
* @return non-zero if two strings are equal, zero otherwise.
*/
int
MHD_str_equal_caseless_ (const char *str1,
const char *str2)
{
while (0 != (*str1))
{
const char c1 = *str1;
const char c2 = *str2;
if (charsequalcaseless (c1, c2))
{
str1++;
str2++;
}
else
return 0;
}
return 0 == (*str2);
}
#endif /* ! MHD_FAVOR_SMALL_CODE */
/**
* Check two string for equality, ignoring case of US-ASCII letters and
* checking not more than @a maxlen characters.
* Compares up to first terminating null character, but not more than
* first @a maxlen characters.
*
* @param str1 first string to compare
* @param str2 second string to compare
* @param maxlen maximum number of characters to compare
* @return non-zero if two strings are equal, zero otherwise.
*/
int
MHD_str_equal_caseless_n_ (const char *const str1,
const char *const str2,
size_t maxlen)
{
size_t i;
for (i = 0; i < maxlen; ++i)
{
const char c1 = str1[i];
const char c2 = str2[i];
if (0 == c2)
return 0 == c1;
if (charsequalcaseless (c1, c2))
continue;
else
return 0;
}
return ! 0;
}
/**
* Check two string for equality, ignoring case of US-ASCII letters and
* checking not more than @a len bytes.
* Compares not more first than @a len bytes, including binary zero characters.
* Comparison stops at first unmatched byte.
* @param str1 first string to compare
* @param str2 second string to compare
* @param len number of characters to compare
* @return non-zero if @a len bytes are equal, zero otherwise.
*/
bool
MHD_str_equal_caseless_bin_n_ (const char *const str1,
const char *const str2,
size_t len)
{
size_t i;
for (i = 0; i < len; ++i)
{
const char c1 = str1[i];
const char c2 = str2[i];
if (charsequalcaseless (c1, c2))
continue;
else
return 0;
}
return ! 0;
}
/**
* Check whether @a str has case-insensitive @a token.
* Token could be surrounded by spaces and tabs and delimited by comma.
* Match succeed if substring between start, end (of string) or comma
* contains only case-insensitive token and optional spaces and tabs.
* @warning token must not contain null-characters except optional
* terminating null-character.
* @param str the string to check
* @param token the token to find
* @param token_len length of token, not including optional terminating
* null-character.
* @return non-zero if two strings are equal, zero otherwise.
*/
bool
MHD_str_has_token_caseless_ (const char *str,
const char *const token,
size_t token_len)
{
if (0 == token_len)
return false;
while (0 != *str)
{
size_t i;
/* Skip all whitespaces and empty tokens. */
while (' ' == *str || '\t' == *str || ',' == *str)
str++;
/* Check for token match. */
i = 0;
while (1)
{
const char sc = *(str++);
const char tc = token[i++];
if (0 == sc)
return false;
if (! charsequalcaseless (sc, tc))
break;
if (i >= token_len)
{
/* Check whether substring match token fully or
* has additional unmatched chars at tail. */
while (' ' == *str || '\t' == *str)
str++;
/* End of (sub)string? */
if ((0 == *str) || (',' == *str) )
return true;
/* Unmatched chars at end of substring. */
break;
}
}
/* Find next substring. */
while (0 != *str && ',' != *str)
str++;
}
return false;
}
/**
* Remove case-insensitive @a token from the @a str and put result
* to the output @a buf.
*
* Tokens in @a str could be surrounded by spaces and tabs and delimited by
* comma. The token match succeed if substring between start, end (of string)
* or comma contains only case-insensitive token and optional spaces and tabs.
* The quoted strings and comments are not supported by this function.
*
* The output string is normalised: empty tokens and repeated whitespaces
* are removed, no whitespaces before commas, exactly one space is used after
* each comma.
*
* @param str the string to process
* @param str_len the length of the @a str, not including optional
* terminating null-character.
* @param token the token to find
* @param token_len the length of @a token, not including optional
* terminating null-character.
* @param[out] buf the output buffer, not null-terminated.
* @param[in,out] buf_size pointer to the size variable, at input it
* is the size of allocated buffer, at output
* it is the size of the resulting string (can
* be up to 50% larger than input) or negative value
* if there is not enough space for the result
* @return 'true' if token has been removed,
* 'false' otherwise.
*/
bool
MHD_str_remove_token_caseless_ (const char *str,
size_t str_len,
const char *const token,
const size_t token_len,
char *buf,
ssize_t *buf_size)
{
const char *s1; /**< the "input" string / character */
char *s2; /**< the "output" string / character */
size_t t_pos; /**< position of matched character in the token */
bool token_removed;
mhd_assert (NULL == memchr (token, 0, token_len));
mhd_assert (NULL == memchr (token, ' ', token_len));
mhd_assert (NULL == memchr (token, '\t', token_len));
mhd_assert (NULL == memchr (token, ',', token_len));
mhd_assert (0 <= *buf_size);
if (SSIZE_MAX <= ((str_len / 2) * 3 + 3))
{
/* The return value may overflow, refuse */
*buf_size = (ssize_t) -1;
return false;
}
s1 = str;
s2 = buf;
token_removed = false;
while ((size_t) (s1 - str) < str_len)
{
const char *cur_token; /**< the first char of current token */
size_t copy_size;
/* Skip any initial whitespaces and empty tokens */
while ( ((size_t) (s1 - str) < str_len) &&
((' ' == *s1) || ('\t' == *s1) || (',' == *s1)) )
s1++;
/* 's1' points to the first char of token in the input string or
* points just beyond the end of the input string */
if ((size_t) (s1 - str) >= str_len)
break; /* Nothing to copy, end of the input string */
/* 's1' points to the first char of token in the input string */
cur_token = s1; /* the first char of input token */
/* Check the token with case-insensetive match */
t_pos = 0;
while ( ((size_t) (s1 - str) < str_len) && (token_len > t_pos) &&
(charsequalcaseless (*s1, token[t_pos])) )
{
s1++;
t_pos++;
}
/* s1 may point just beyond the end of the input string */
if ( (token_len == t_pos) && (0 != token_len) )
{
/* 'token' matched, check that current input token does not have
* any suffixes */
while ( ((size_t) (s1 - str) < str_len) &&
((' ' == *s1) || ('\t' == *s1)) )
s1++;
/* 's1' points to the first non-whitespace char after the token matched
* requested token or points just beyond the end of the input string after
* the requested token */
if (((size_t) (s1 - str) == str_len) || (',' == *s1))
{/* full token match, do not copy current token to the output */
token_removed = true;
continue;
}
}
/* 's1' points to first non-whitespace char, to some char after
* first non-whitespace char in the token in the input string, to
* the ',', or just beyond the end of the input string */
/* The current token in the input string does not match the token
* to exclude, it must be copied to the output string */
/* the current token size excluding leading whitespaces and current char */
copy_size = (size_t) (s1 - cur_token);
if (buf == s2)
{ /* The first token to copy to the output */
if ((size_t) *buf_size < copy_size)
{ /* Not enough space in the output buffer */
*buf_size = (ssize_t) -1;
return false;
}
}
else
{ /* Some token was already copied to the output buffer */
mhd_assert (s2 > buf);
if ((size_t) *buf_size < ((size_t) (s2 - buf)) + copy_size + 2)
{ /* Not enough space in the output buffer */
*buf_size = (ssize_t) -1;
return false;
}
*(s2++) = ',';
*(s2++) = ' ';
}
/* Copy non-matched token to the output */
if (0 != copy_size)
{
memcpy (s2, cur_token, copy_size);
s2 += copy_size;
}
while ( ((size_t) (s1 - str) < str_len) && (',' != *s1))
{
/* 's1' points to first non-whitespace char, to some char after
* first non-whitespace char in the token in the input string */
/* Copy all non-whitespace chars from the current token in
* the input string */
while ( ((size_t) (s1 - str) < str_len) &&
(',' != *s1) && (' ' != *s1) && ('\t' != *s1) )
{
mhd_assert (s2 >= buf);
if ((size_t) *buf_size <= (size_t) (s2 - buf)) /* '<= s2' equals '< s2 + 1' */
{ /* Not enough space in the output buffer */
*buf_size = (ssize_t) -1;
return false;
}
*(s2++) = *(s1++);
}
/* 's1' points to some whitespace char in the token in the input
* string, to the ',', or just beyond the end of the input string */
/* Skip all whitespaces */
while ( ((size_t) (s1 - str) < str_len) &&
((' ' == *s1) || ('\t' == *s1)) )
s1++;
/* 's1' points to the first non-whitespace char in the input string
* after whitespace chars, to the ',', or just beyond the end of
* the input string */
if (((size_t) (s1 - str) < str_len) && (',' != *s1))
{ /* Not the end of the current token */
mhd_assert (s2 >= buf);
if ((size_t) *buf_size <= (size_t) (s2 - buf)) /* '<= s2' equals '< s2 + 1' */
{ /* Not enough space in the output buffer */
*buf_size = (ssize_t) -1;
return false;
}
*(s2++) = ' ';
}
}
}
mhd_assert (((ssize_t) (s2 - buf)) <= *buf_size);
*buf_size = (ssize_t) (s2 - buf);
return token_removed;
}
/**
* Perform in-place case-insensitive removal of @a tokens from the @a str.
*
* Token could be surrounded by spaces and tabs and delimited by comma.
* The token match succeed if substring between start, end (of the string), or
* comma contains only case-insensitive token and optional spaces and tabs.
* The quoted strings and comments are not supported by this function.
*
* The input string must be normalised: empty tokens and repeated whitespaces
* are removed, no whitespaces before commas, exactly one space is used after
* each comma. The string is updated in-place.
*
* Behavior is undefined is the input string in not normalised.
*
* @param[in,out] str the string to update
* @param[in,out] str_len the length of the @a str, not including optional
* terminating null-character, not null-terminated
* @param tokens the token to find
* @param tokens_len the length of @a tokens, not including optional
* terminating null-character.
* @return 'true' if any token has been removed,
* 'false' otherwise.
*/
bool
MHD_str_remove_tokens_caseless_ (char *str,
size_t *str_len,
const char *const tokens,
const size_t tokens_len)
{
const char *const t = tokens; /**< a short alias for @a tokens */
size_t pt; /**< position in @a tokens */
bool token_removed;
mhd_assert (NULL == memchr (tokens, 0, tokens_len));
token_removed = false;
pt = 0;
while (pt < tokens_len && *str_len != 0)
{
const char *tkn; /**< the current token */
size_t tkn_len;
/* Skip any initial whitespaces and empty tokens in 'tokens' */
while ( (pt < tokens_len) &&
((' ' == t[pt]) || ('\t' == t[pt]) || (',' == t[pt])) )
pt++;
if (pt >= tokens_len)
break; /* No more tokens, nothing to remove */
/* Found non-whitespace char which is not a comma */
tkn = t + pt;
do
{
do
{
pt++;
} while (pt < tokens_len &&
(' ' != t[pt] && '\t' != t[pt] && ',' != t[pt]));
/* Found end of the token string, space, tab, or comma */
tkn_len = pt - (size_t) (tkn - t);
/* Skip all spaces and tabs */
while (pt < tokens_len && (' ' == t[pt] || '\t' == t[pt]))
pt++;
/* Found end of the token string or non-whitespace char */
} while(pt < tokens_len && ',' != t[pt]);
/* 'tkn' is the input token with 'tkn_len' chars */
mhd_assert (0 != tkn_len);
if (*str_len == tkn_len)
{
if (MHD_str_equal_caseless_bin_n_ (str, tkn, tkn_len))
{
*str_len = 0;
token_removed = true;
}
continue;
}
/* 'tkn' cannot match part of 'str' if length of 'tkn' is larger
* than length of 'str'.
* It's know that 'tkn' is not equal to the 'str' (was checked previously).
* As 'str' is normalized when 'tkn' is not equal to the 'str'
* it is required that 'str' to be at least 3 chars larger then 'tkn'
* (the comma, the space and at least one additional character for the next
* token) to remove 'tkn' from the 'str'. */
if (*str_len > tkn_len + 2)
{ /* Remove 'tkn' from the input string */
size_t pr; /**< the 'read' position in the @a str */
size_t pw; /**< the 'write' position in the @a str */
pr = 0;
pw = 0;
do
{
mhd_assert (pr >= pw);
mhd_assert ((*str_len) >= (pr + tkn_len));
if ( ( ((*str_len) == (pr + tkn_len)) || (',' == str[pr + tkn_len]) ) &&
MHD_str_equal_caseless_bin_n_ (str + pr, tkn, tkn_len) )
{
/* current token in the input string matches the 'tkn', skip it */
mhd_assert ((*str_len == pr + tkn_len) || \
(' ' == str[pr + tkn_len + 1])); /* 'str' must be normalized */
token_removed = true;
/* Advance to the next token in the input string or beyond
* the end of the input string. */
pr += tkn_len + 2;
}
else
{
/* current token in the input string does not match the 'tkn',
* copy to the output */
if (0 != pw)
{ /* not the first output token, add ", " to separate */
if (pr != pw + 2)
{
str[pw++] = ',';
str[pw++] = ' ';
}
else
pw += 2; /* 'str' is not yet modified in this round */
}
do
{
if (pr != pw)
str[pw] = str[pr];
pr++;
pw++;
} while (pr < *str_len && ',' != str[pr]);
/* Advance to the next token in the input string or beyond
* the end of the input string. */
pr += 2;
}
/* 'pr' should point to the next token in the input string or beyond
* the end of the input string */
if ((*str_len) < (pr + tkn_len))
{ /* The rest of the 'str + pr' is too small to match 'tkn' */
if ((*str_len) > pr)
{ /* Copy the rest of the string */
size_t copy_size;
copy_size = *str_len - pr;
if (0 != pw)
{ /* not the first output token, add ", " to separate */
if (pr != pw + 2)
{
str[pw++] = ',';
str[pw++] = ' ';
}
else
pw += 2; /* 'str' is not yet modified in this round */
}
if (pr != pw)
memmove (str + pw, str + pr, copy_size);
pw += copy_size;
}
*str_len = pw;
break;
}
mhd_assert ((' ' != str[0]) && ('\t' != str[0]));
mhd_assert ((0 == pr) || (3 <= pr));
mhd_assert ((0 == pr) || (' ' == str[pr - 1]));
mhd_assert ((0 == pr) || (',' == str[pr - 2]));
} while (1);
}
}
return token_removed;
}
#ifndef MHD_FAVOR_SMALL_CODE
/* Use individual function for each case */
/**
* Convert decimal US-ASCII digits in string to number in uint64_t.
* Conversion stopped at first non-digit character.
*
* @param str string to convert
* @param[out] out_val pointer to uint64_t to store result of conversion
* @return non-zero number of characters processed on succeed,
* zero if no digit is found, resulting value is larger
* then possible to store in uint64_t or @a out_val is NULL
*/
size_t
MHD_str_to_uint64_ (const char *str,
uint64_t *out_val)
{
const char *const start = str;
uint64_t res;
if (! str || ! out_val || ! isasciidigit (str[0]))
return 0;
res = 0;
do
{
const int digit = (unsigned char) (*str) - '0';
if ( (res > (UINT64_MAX / 10)) ||
( (res == (UINT64_MAX / 10)) &&
((uint64_t) digit > (UINT64_MAX % 10)) ) )
return 0;
res *= 10;
res += (unsigned int) digit;
str++;
} while (isasciidigit (*str));
*out_val = res;
return (size_t) (str - start);
}
/**
* Convert not more then @a maxlen decimal US-ASCII digits in string to
* number in uint64_t.
* Conversion stopped at first non-digit character or after @a maxlen
* digits.
*
* @param str string to convert
* @param maxlen maximum number of characters to process
* @param[out] out_val pointer to uint64_t to store result of conversion
* @return non-zero number of characters processed on succeed,
* zero if no digit is found, resulting value is larger
* then possible to store in uint64_t or @a out_val is NULL
*/
size_t
MHD_str_to_uint64_n_ (const char *str,
size_t maxlen,
uint64_t *out_val)
{
uint64_t res;
size_t i;
if (! str || ! maxlen || ! out_val || ! isasciidigit (str[0]))
return 0;
res = 0;
i = 0;
do
{
const int digit = (unsigned char) str[i] - '0';
if ( (res > (UINT64_MAX / 10)) ||
( (res == (UINT64_MAX / 10)) &&
((uint64_t) digit > (UINT64_MAX % 10)) ) )
return 0;
res *= 10;
res += (unsigned int) digit;
i++;
} while ( (i < maxlen) &&
isasciidigit (str[i]) );
*out_val = res;
return i;
}
/**
* Convert hexadecimal US-ASCII digits in string to number in uint32_t.
* Conversion stopped at first non-digit character.
*
* @param str string to convert
* @param[out] out_val pointer to uint32_t to store result of conversion
* @return non-zero number of characters processed on succeed,
* zero if no digit is found, resulting value is larger
* then possible to store in uint32_t or @a out_val is NULL
*/
size_t
MHD_strx_to_uint32_ (const char *str,
uint32_t *out_val)
{
const char *const start = str;
uint32_t res;
int digit;
if (! str || ! out_val)
return 0;
res = 0;
digit = toxdigitvalue (*str);
while (digit >= 0)
{
if ( (res < (UINT32_MAX / 16)) ||
((res == (UINT32_MAX / 16)) && ( (uint32_t) digit <= (UINT32_MAX
% 16)) ) )
{
res *= 16;
res += (unsigned int) digit;
}
else
return 0;
str++;
digit = toxdigitvalue (*str);
}
if (str - start > 0)
*out_val = res;
return (size_t) (str - start);
}
/**
* Convert not more then @a maxlen hexadecimal US-ASCII digits in string
* to number in uint32_t.
* Conversion stopped at first non-digit character or after @a maxlen
* digits.
*
* @param str string to convert
* @param maxlen maximum number of characters to process
* @param[out] out_val pointer to uint32_t to store result of conversion
* @return non-zero number of characters processed on succeed,
* zero if no digit is found, resulting value is larger
* then possible to store in uint32_t or @a out_val is NULL
*/
size_t
MHD_strx_to_uint32_n_ (const char *str,
size_t maxlen,
uint32_t *out_val)
{
size_t i;
uint32_t res;
int digit;
if (! str || ! out_val)
return 0;
res = 0;
i = 0;
while (i < maxlen && (digit = toxdigitvalue (str[i])) >= 0)
{
if ( (res > (UINT32_MAX / 16)) ||
((res == (UINT32_MAX / 16)) && ( (uint32_t) digit > (UINT32_MAX
% 16)) ) )
return 0;
res *= 16;
res += (unsigned int) digit;
i++;
}
if (i)
*out_val = res;
return i;
}
/**
* Convert hexadecimal US-ASCII digits in string to number in uint64_t.
* Conversion stopped at first non-digit character.
*
* @param str string to convert
* @param[out] out_val pointer to uint64_t to store result of conversion
* @return non-zero number of characters processed on succeed,
* zero if no digit is found, resulting value is larger
* then possible to store in uint64_t or @a out_val is NULL
*/
size_t
MHD_strx_to_uint64_ (const char *str,
uint64_t *out_val)
{
const char *const start = str;
uint64_t res;
int digit;
if (! str || ! out_val)
return 0;
res = 0;
digit = toxdigitvalue (*str);
while (digit >= 0)
{
if ( (res < (UINT64_MAX / 16)) ||
((res == (UINT64_MAX / 16)) && ( (uint64_t) digit <= (UINT64_MAX
% 16)) ) )
{
res *= 16;
res += (unsigned int) digit;
}
else
return 0;
str++;
digit = toxdigitvalue (*str);
}
if (str - start > 0)
*out_val = res;
return (size_t) (str - start);
}
/**
* Convert not more then @a maxlen hexadecimal US-ASCII digits in string
* to number in uint64_t.
* Conversion stopped at first non-digit character or after @a maxlen
* digits.
*
* @param str string to convert
* @param maxlen maximum number of characters to process
* @param[out] out_val pointer to uint64_t to store result of conversion
* @return non-zero number of characters processed on succeed,
* zero if no digit is found, resulting value is larger
* then possible to store in uint64_t or @a out_val is NULL
*/
size_t
MHD_strx_to_uint64_n_ (const char *str,
size_t maxlen,
uint64_t *out_val)
{
size_t i;
uint64_t res;
int digit;
if (! str || ! out_val)
return 0;
res = 0;
i = 0;
while (i < maxlen && (digit = toxdigitvalue (str[i])) >= 0)
{
if ( (res > (UINT64_MAX / 16)) ||
((res == (UINT64_MAX / 16)) && ( (uint64_t) digit > (UINT64_MAX
% 16)) ) )
return 0;
res *= 16;
res += (unsigned int) digit;
i++;
}
if (i)
*out_val = res;
return i;
}
#else /* MHD_FAVOR_SMALL_CODE */
/**
* Generic function for converting not more then @a maxlen
* hexadecimal or decimal US-ASCII digits in string to number.
* Conversion stopped at first non-digit character or after @a maxlen
* digits.
* To be used only within macro.
*
* @param str the string to convert
* @param maxlen the maximum number of characters to process
* @param out_val the pointer to variable to store result of conversion
* @param val_size the size of variable pointed by @a out_val, in bytes, 4 or 8
* @param max_val the maximum decoded number
* @param base the numeric base, 10 or 16
* @return non-zero number of characters processed on succeed,
* zero if no digit is found, resulting value is larger
* then @a max_val, @a val_size is not 4/8 or @a out_val is NULL
*/
size_t
MHD_str_to_uvalue_n_ (const char *str,
size_t maxlen,
void *out_val,
size_t val_size,
uint64_t max_val,
unsigned int base)
{
size_t i;
uint64_t res;
const uint64_t max_v_div_b = max_val / base;
const uint64_t max_v_mod_b = max_val % base;
if (! str || ! out_val ||
((base != 16) && (base != 10)) )
return 0;
res = 0;
i = 0;
while (maxlen > i)
{
const int digit = (base == 16) ?
toxdigitvalue (str[i]) : todigitvalue (str[i]);
if (0 > digit)
break;
if ( ((max_v_div_b) < res) ||
(( (max_v_div_b) == res) && ( (max_v_mod_b) < (uint64_t) digit) ) )
return 0;
res *= base;
res += (unsigned int) digit;
i++;
}
if (i)
{
if (8 == val_size)
*(uint64_t *) out_val = res;
else if (4 == val_size)
*(uint32_t *) out_val = (uint32_t) res;
else
return 0;
}
return i;
}
#endif /* MHD_FAVOR_SMALL_CODE */
size_t
MHD_uint32_to_strx (uint32_t val,
char *buf,
size_t buf_size)
{
size_t o_pos = 0; /**< position of the output character */
int digit_pos = 8; /** zero-based, digit position in @a 'val' */
int digit;
/* Skip leading zeros */
do
{
digit_pos--;
digit = (int) (val >> 28);
val <<= 4;
} while ((0 == digit) && (0 != digit_pos));
while (o_pos < buf_size)
{
buf[o_pos++] = (digit <= 9) ? ('0' + (char) digit) :
('A' + (char) digit - 10);
if (0 == digit_pos)
return o_pos;
digit_pos--;
digit = (int) (val >> 28);
val <<= 4;
}
return 0; /* The buffer is too small */
}
#ifndef MHD_FAVOR_SMALL_CODE
size_t
MHD_uint16_to_str (uint16_t val,
char *buf,
size_t buf_size)
{
char *chr; /**< pointer to the current printed digit */
/* The biggest printable number is 65535 */
uint16_t divisor = UINT16_C (10000);
int digit;
chr = buf;
digit = (int) (val / divisor);
mhd_assert (digit < 10);
/* Do not print leading zeros */
while ((0 == digit) && (1 < divisor))
{
divisor /= 10;
digit = (int) (val / divisor);
mhd_assert (digit < 10);
}
while (0 != buf_size)
{
*chr = (char) digit + '0';
chr++;
buf_size--;
if (1 == divisor)
return (size_t) (chr - buf);
val %= divisor;
divisor /= 10;
digit = (int) (val / divisor);
mhd_assert (digit < 10);
}
return 0; /* The buffer is too small */
}
#endif /* !MHD_FAVOR_SMALL_CODE */
size_t
MHD_uint64_to_str (uint64_t val,
char *buf,
size_t buf_size)
{
char *chr; /**< pointer to the current printed digit */
/* The biggest printable number is 18446744073709551615 */
uint64_t divisor = UINT64_C (10000000000000000000);
int digit;
chr = buf;
digit = (int) (val / divisor);
mhd_assert (digit < 10);
/* Do not print leading zeros */
while ((0 == digit) && (1 < divisor))
{
divisor /= 10;
digit = (int) (val / divisor);
mhd_assert (digit < 10);
}
while (0 != buf_size)
{
*chr = (char) digit + '0';
chr++;
buf_size--;
if (1 == divisor)
return (size_t) (chr - buf);
val %= divisor;
divisor /= 10;
digit = (int) (val / divisor);
mhd_assert (digit < 10);
}
return 0; /* The buffer is too small */
}
size_t
MHD_uint8_to_str_pad (uint8_t val,
uint8_t min_digits,
char *buf,
size_t buf_size)
{
size_t pos; /**< the position of the current printed digit */
int digit;
mhd_assert (3 >= min_digits);
if (0 == buf_size)
return 0;
pos = 0;
digit = val / 100;
if (0 == digit)
{
if (3 <= min_digits)
buf[pos++] = '0';
}
else
{
buf[pos++] = '0' + (char) digit;
val %= 100;
min_digits = 2;
}
if (buf_size <= pos)
return 0;
digit = val / 10;
if (0 == digit)
{
if (2 <= min_digits)
buf[pos++] = '0';
}
else
{
buf[pos++] = '0' + (char) digit;
val %= 10;
}
if (buf_size <= pos)
return 0;
buf[pos++] = '0' + (char) val;
return pos;
}
size_t
MHD_bin_to_hex (const void *bin,
size_t size,
char *hex)
{
size_t i;
for (i = 0; i < size; ++i)
{
uint8_t j;
const uint8_t b = ((const uint8_t *) bin)[i];
j = b >> 4;
hex[i * 2] = (char) ((j < 10) ? (j + '0') : (j - 10 + 'a'));
j = b & 0x0f;
hex[i * 2 + 1] = (char) ((j < 10) ? (j + '0') : (j - 10 + 'a'));
}
return i * 2;
}
size_t
MHD_bin_to_hex_z (const void *bin,
size_t size,
char *hex)
{
size_t res;
res = MHD_bin_to_hex (bin, size, hex);
hex[res] = 0;
return res;
}
size_t
MHD_hex_to_bin (const char *hex,
size_t len,
void *bin)
{
uint8_t *const out = (uint8_t *) bin;
size_t r;
size_t w;
if (0 == len)
return 0;
r = 0;
w = 0;
if (0 != len % 2)
{
/* Assume the first byte is encoded with single digit */
const int l = toxdigitvalue (hex[r++]);
if (0 > l)
return 0;
out[w++] = (uint8_t) ((unsigned int) l);
}
while (r < len)
{
const int h = toxdigitvalue (hex[r++]);
const int l = toxdigitvalue (hex[r++]);
if ((0 > h) || (0 > l))
return 0;
out[w++] = ( ((uint8_t) (((uint8_t) ((unsigned int) h)) << 4))
| ((uint8_t) ((unsigned int) l)) );
}
mhd_assert (len == r);
mhd_assert ((len + 1) / 2 == w);
return w;
}
size_t
MHD_str_pct_decode_strict_n_ (const char *pct_encoded,
size_t pct_encoded_len,
char *decoded,
size_t buf_size)
{
#ifdef MHD_FAVOR_SMALL_CODE
bool broken;
size_t res;
res = MHD_str_pct_decode_lenient_n_ (pct_encoded, pct_encoded_len, decoded,
buf_size, &broken);
if (broken)
return 0;
return res;
#else /* ! MHD_FAVOR_SMALL_CODE */
size_t r;
size_t w;
r = 0;
w = 0;
if (buf_size >= pct_encoded_len)
{
while (r < pct_encoded_len)
{
const char chr = pct_encoded[r];
if ('%' == chr)
{
if (2 > pct_encoded_len - r)
return 0;
else
{
const int h = toxdigitvalue (pct_encoded[++r]);
const int l = toxdigitvalue (pct_encoded[++r]);
unsigned char out;
if ((0 > h) || (0 > l))
return 0;
out = (unsigned char) ( (((uint8_t) ((unsigned int) h)) << 4)
| ((uint8_t) ((unsigned int) l)) );
decoded[w] = (char) out;
}
}
else
decoded[w] = chr;
++r;
++w;
}
return w;
}
while (r < pct_encoded_len)
{
const char chr = pct_encoded[r];
if (w >= buf_size)
return 0;
if ('%' == chr)
{
if (2 > pct_encoded_len - r)
return 0;
else
{
const int h = toxdigitvalue (pct_encoded[++r]);
const int l = toxdigitvalue (pct_encoded[++r]);
unsigned char out;
if ((0 > h) || (0 > l))
return 0;
out = (unsigned char) ( (((uint8_t) ((unsigned int) h)) << 4)
| ((uint8_t) ((unsigned int) l)) );
decoded[w] = (char) out;
}
}
else
decoded[w] = chr;
++r;
++w;
}
return w;
#endif /* ! MHD_FAVOR_SMALL_CODE */
}
size_t
MHD_str_pct_decode_lenient_n_ (const char *pct_encoded,
size_t pct_encoded_len,
char *decoded,
size_t buf_size,
bool *broken_encoding)
{
size_t r;
size_t w;
r = 0;
w = 0;
if (NULL != broken_encoding)
*broken_encoding = false;
#ifndef MHD_FAVOR_SMALL_CODE
if (buf_size >= pct_encoded_len)
{
while (r < pct_encoded_len)
{
const char chr = pct_encoded[r];
if ('%' == chr)
{
if (2 > pct_encoded_len - r)
{
if (NULL != broken_encoding)
*broken_encoding = true;
decoded[w] = chr; /* Copy "as is" */
}
else
{
const int h = toxdigitvalue (pct_encoded[++r]);
const int l = toxdigitvalue (pct_encoded[++r]);
unsigned char out;
if ((0 > h) || (0 > l))
{
r -= 2;
if (NULL != broken_encoding)
*broken_encoding = true;
decoded[w] = chr; /* Copy "as is" */
}
else
{
out = (unsigned char) ( (((uint8_t) ((unsigned int) h)) << 4)
| ((uint8_t) ((unsigned int) l)) );
decoded[w] = (char) out;
}
}
}
else
decoded[w] = chr;
++r;
++w;
}
return w;
}
#endif /* ! MHD_FAVOR_SMALL_CODE */
while (r < pct_encoded_len)
{
const char chr = pct_encoded[r];
if (w >= buf_size)
return 0;
if ('%' == chr)
{
if (2 > pct_encoded_len - r)
{
if (NULL != broken_encoding)
*broken_encoding = true;
decoded[w] = chr; /* Copy "as is" */
}
else
{
const int h = toxdigitvalue (pct_encoded[++r]);
const int l = toxdigitvalue (pct_encoded[++r]);
if ((0 > h) || (0 > l))
{
r -= 2;
if (NULL != broken_encoding)
*broken_encoding = true;
decoded[w] = chr; /* Copy "as is" */
}
else
{
unsigned char out;
out = (unsigned char) ( (((uint8_t) ((unsigned int) h)) << 4)
| ((uint8_t) ((unsigned int) l)) );
decoded[w] = (char) out;
}
}
}
else
decoded[w] = chr;
++r;
++w;
}
return w;
}
size_t
MHD_str_pct_decode_in_place_strict_ (char *str)
{
#ifdef MHD_FAVOR_SMALL_CODE
size_t res;
bool broken;
res = MHD_str_pct_decode_in_place_lenient_ (str, &broken);
if (broken)
{
res = 0;
str[0] = 0;
}
return res;
#else /* ! MHD_FAVOR_SMALL_CODE */
size_t r;
size_t w;
r = 0;
w = 0;
while (0 != str[r])
{
const char chr = str[r++];
if ('%' == chr)
{
const char d1 = str[r++];
if (0 == d1)
return 0;
else
{
const char d2 = str[r++];
if (0 == d2)
return 0;
else
{
const int h = toxdigitvalue (d1);
const int l = toxdigitvalue (d2);
unsigned char out;
if ((0 > h) || (0 > l))
return 0;
out = (unsigned char) ( (((uint8_t) ((unsigned int) h)) << 4)
| ((uint8_t) ((unsigned int) l)) );
str[w++] = (char) out;
}
}
}
else
str[w++] = chr;
}
str[w] = 0;
return w;
#endif /* ! MHD_FAVOR_SMALL_CODE */
}
size_t
MHD_str_pct_decode_in_place_lenient_ (char *str,
bool *broken_encoding)
{
#ifdef MHD_FAVOR_SMALL_CODE
size_t len;
size_t res;
len = strlen (str);
res = MHD_str_pct_decode_lenient_n_ (str, len, str, len, broken_encoding);
str[res] = 0;
return res;
#else /* ! MHD_FAVOR_SMALL_CODE */
size_t r;
size_t w;
if (NULL != broken_encoding)
*broken_encoding = false;
r = 0;
w = 0;
while (0 != str[r])
{
const char chr = str[r++];
if ('%' == chr)
{
const char d1 = str[r++];
if (0 == d1)
{
if (NULL != broken_encoding)
*broken_encoding = true;
str[w++] = chr; /* Copy "as is" */
str[w] = 0;
return w;
}
else
{
const char d2 = str[r++];
if (0 == d2)
{
if (NULL != broken_encoding)
*broken_encoding = true;
str[w++] = chr; /* Copy "as is" */
str[w++] = d1; /* Copy "as is" */
str[w] = 0;
return w;
}
else
{
const int h = toxdigitvalue (d1);
const int l = toxdigitvalue (d2);
unsigned char out;
if ((0 > h) || (0 > l))
{
if (NULL != broken_encoding)
*broken_encoding = true;
str[w++] = chr; /* Copy "as is" */
str[w++] = d1;
str[w++] = d2;
continue;
}
out = (unsigned char) ( (((uint8_t) ((unsigned int) h)) << 4)
| ((uint8_t) ((unsigned int) l)) );
str[w++] = (char) out;
continue;
}
}
}
str[w++] = chr;
}
str[w] = 0;
return w;
#endif /* ! MHD_FAVOR_SMALL_CODE */
}
#ifdef DAUTH_SUPPORT
bool
MHD_str_equal_quoted_bin_n (const char *quoted,
size_t quoted_len,
const char *unquoted,
size_t unquoted_len)
{
size_t i;
size_t j;
if (unquoted_len < quoted_len / 2)
return false;
j = 0;
for (i = 0; quoted_len > i && unquoted_len > j; ++i, ++j)
{
if ('\\' == quoted[i])
{
i++; /* Advance to the next character */
if (quoted_len == i)
return false; /* No character after escaping backslash */
}
if (quoted[i] != unquoted[j])
return false; /* Different characters */
}
if ((quoted_len != i) || (unquoted_len != j))
return false; /* The strings have different length */
return true;
}
bool
MHD_str_equal_caseless_quoted_bin_n (const char *quoted,
size_t quoted_len,
const char *unquoted,
size_t unquoted_len)
{
size_t i;
size_t j;
if (unquoted_len < quoted_len / 2)
return false;
j = 0;
for (i = 0; quoted_len > i && unquoted_len > j; ++i, ++j)
{
if ('\\' == quoted[i])
{
i++; /* Advance to the next character */
if (quoted_len == i)
return false; /* No character after escaping backslash */
}
if (! charsequalcaseless (quoted[i], unquoted[j]))
return false; /* Different characters */
}
if ((quoted_len != i) || (unquoted_len != j))
return false; /* The strings have different length */
return true;
}
size_t
MHD_str_unquote (const char *quoted,
size_t quoted_len,
char *result)
{
size_t r;
size_t w;
r = 0;
w = 0;
while (quoted_len > r)
{
if ('\\' == quoted[r])
{
++r;
if (quoted_len == r)
return 0; /* Last backslash is not followed by char to unescape */
}
result[w++] = quoted[r++];
}
return w;
}
#endif /* DAUTH_SUPPORT */
#if defined(DAUTH_SUPPORT) || defined(BAUTH_SUPPORT)
size_t
MHD_str_quote (const char *unquoted,
size_t unquoted_len,
char *result,
size_t buf_size)
{
size_t r;
size_t w;
r = 0;
w = 0;
#ifndef MHD_FAVOR_SMALL_CODE
if (unquoted_len * 2 <= buf_size)
{
/* Fast loop: the output will fit the buffer with any input string content */
while (unquoted_len > r)
{
const char chr = unquoted[r++];
if (('\\' == chr) || ('\"' == chr))
result[w++] = '\\'; /* Escape current char */
result[w++] = chr;
}
}
else
{
if (unquoted_len > buf_size)
return 0; /* Quick fail: the output buffer is too small */
#else /* MHD_FAVOR_SMALL_CODE */
if (1)
{
#endif /* MHD_FAVOR_SMALL_CODE */
while (unquoted_len > r)
{
if (buf_size <= w)
return 0; /* The output buffer is too small */
else
{
const char chr = unquoted[r++];
if (('\\' == chr) || ('\"' == chr))
{
result[w++] = '\\'; /* Escape current char */
if (buf_size <= w)
return 0; /* The output buffer is too small */
}
result[w++] = chr;
}
}
}
mhd_assert (w >= r);
mhd_assert (w <= r * 2);
return w;
}
#endif /* DAUTH_SUPPORT || BAUTH_SUPPORT */
#ifdef BAUTH_SUPPORT
size_t
MHD_base64_to_bin_n (const char *base64,
size_t base64_len,
void *bin,
size_t bin_size)
{
#ifndef MHD_FAVOR_SMALL_CODE
#define map_type int
#else /* MHD_FAVOR_SMALL_CODE */
#define map_type int8_t
#endif /* MHD_FAVOR_SMALL_CODE */
static const map_type map[] = {
/* -1 = invalid char, -2 = padding
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, A, B, C, D, E, F */
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 00..0F */
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 10..1F */
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, /* 20..2F */
52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -2, -1, -1, /* 30..3F */
-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, /* 40..4F */
15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, /* 50..5F */
-1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, /* 60..6F */
41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1 /* 70..7F */
#ifndef MHD_FAVOR_SMALL_CODE
,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 80..8F */
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 90..9F */
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* A0..AF */
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* B0..BF */
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* C0..CF */
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* D0..DF */
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* E0..EF */
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* F0..FF */
#endif /* ! MHD_FAVOR_SMALL_CODE */
};
const uint8_t *const in = (const uint8_t *) base64;
uint8_t *const out = (uint8_t *) bin;
size_t i;
size_t j;
if (0 == base64_len)
return 0; /* Nothing to decode */
if (0 != base64_len % 4)
return 0; /* Wrong input length */
if (base64_len / 4 * 3 - 2 > bin_size)
return 0;
j = 0;
for (i = 0; i < (base64_len - 4); i += 4)
{
#ifdef MHD_FAVOR_SMALL_CODE
if (0 != (0x80 & (in[i] | in[i + 1] | in[i + 2] | in[i + 3])))
return 0;
#endif /* MHD_FAVOR_SMALL_CODE */
if (1)
{
const map_type v1 = map[in[i + 0]];
const map_type v2 = map[in[i + 1]];
const map_type v3 = map[in[i + 2]];
const map_type v4 = map[in[i + 3]];
if ((0 > v1) || (0 > v2) || (0 > v3) || (0 > v4))
return 0;
out[j + 0] = (uint8_t) ((((uint8_t) v1) << 2) | (((uint8_t) v2) >> 4));
out[j + 1] = (uint8_t) ((((uint8_t) v2) << 4) | (((uint8_t) v3) >> 2));
out[j + 2] = (uint8_t) ((((uint8_t) v3) << 6) | (((uint8_t) v4)));
}
j += 3;
}
#ifdef MHD_FAVOR_SMALL_CODE
if (0 != (0x80 & (in[i] | in[i + 1] | in[i + 2] | in[i + 3])))
return 0;
#endif /* MHD_FAVOR_SMALL_CODE */
if (1)
{ /* The last four chars block */
const map_type v1 = map[in[i + 0]];
const map_type v2 = map[in[i + 1]];
const map_type v3 = map[in[i + 2]];
const map_type v4 = map[in[i + 3]];
if ((0 > v1) || (0 > v2))
return 0; /* Invalid char or padding at first two positions */
mhd_assert (j < bin_size);
out[j++] = (uint8_t) ((((uint8_t) v1) << 2) | (((uint8_t) v2) >> 4));
if (0 > v3)
{ /* Third char is either padding or invalid */
if ((-2 != v3) || (-2 != v4))
return 0; /* Both two last chars must be padding */
if (0 != (uint8_t) (((uint8_t) v2) << 4))
return 0; /* Wrong last char */
return j;
}
if (j >= bin_size)
return 0; /* Not enough space */
out[j++] = (uint8_t) ((((uint8_t) v2) << 4) | (((uint8_t) v3) >> 2));
if (0 > v4)
{ /* Fourth char is either padding or invalid */
if (-2 != v4)
return 0; /* The char must be padding */
if (0 != (uint8_t) (((uint8_t) v3) << 6))
return 0; /* Wrong last char */
return j;
}
if (j >= bin_size)
return 0; /* Not enough space */
out[j++] = (uint8_t) ((((uint8_t) v3) << 6) | (((uint8_t) v4)));
}
return j;
#undef map_type
}
#endif /* BAUTH_SUPPORT */