PR #23227 opened by Leo Izen (Traneptora) URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/23227 Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/23227.patch
Until this point there has been no API to handle encoding and decoding hex strings in libavutil. This allows us to centralize much of the hex encode/decode logic. Signed-off-by: Leo Izen <[email protected]> >From 7857fff7f4cbebc3c647817516b57783c0f410a8 Mon Sep 17 00:00:00 2001 From: Leo Izen <[email protected]> Date: Mon, 25 May 2026 09:47:43 -0400 Subject: [PATCH] avutil/hex: add hex encode/decode API Until this point there has been no API to handle encoding and decoding hex strings in libavutil. This allows us to centralize much of the hex encode/decode logic. Signed-off-by: Leo Izen <[email protected]> --- libavutil/hex.c | 147 ++++++++++++++++++++++++++++++++++++++++++++ libavutil/hex.h | 93 ++++++++++++++++++++++++++++ libavutil/version.h | 2 +- 3 files changed, 241 insertions(+), 1 deletion(-) create mode 100644 libavutil/hex.c create mode 100644 libavutil/hex.h diff --git a/libavutil/hex.c b/libavutil/hex.c new file mode 100644 index 0000000000..5bccde3b24 --- /dev/null +++ b/libavutil/hex.c @@ -0,0 +1,147 @@ +/* + * Copyright (c) 2026 Leo Izen + * + * This file is part of FFmpeg. + * + * FFmpeg 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. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <errno.h> +#include <stdint.h> + +#include "avutil.h" +#include "hex.h" + +static const uint8_t hex_lowercase_lut[16] = { + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', +}; + +static const uint8_t hex_uppercase_lut[16] = { + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', +}; + +static const uint8_t hex_lut[256] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0, + 0, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +}; + +static const uint8_t hex_valid_upper_lut[256] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, + 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +}; + +static const uint8_t hex_valid_lower_lut[256] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +}; + +static const uint8_t hex_valid_both_lut[256] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, + 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +}; + +int64_t av_hex_decode(uint8_t *out, size_t out_size, const uint8_t *in, size_t in_size, int flags) +{ + const uint8_t *valid_lut; + int check_case = 0; + const uint8_t *const in_start = in; + const uint8_t *in_end = in + in_size; + const uint8_t *out_end = out + out_size; + int out_parity = 0; + + if ((flags & AV_HEX_WANT_LOWERCASE) && !(flags & AV_HEX_WANT_UPPERCASE)) { + valid_lut = hex_valid_lower_lut; + } else if ((flags & AV_HEX_WANT_UPPERCASE) && !(flags & AV_HEX_WANT_LOWERCASE)) { + valid_lut = hex_valid_upper_lut; + } else if (!(flags & AV_HEX_WANT_LOWERCASE) && !(flags & AV_HEX_WANT_UPPERCASE)) { + valid_lut = hex_valid_both_lut; + } else { + check_case = 1; + valid_lut = hex_valid_both_lut; + } + + while (out < out_end && in < in_end) { + if (!valid_lut[*in]) { + if (flags & AV_HEX_IGNORE_INVALID) { + in++; + continue; + } + return out_size - (out_end - out); + } + if (check_case) { + if (hex_valid_upper_lut[*in] && !hex_valid_lower_lut[*in]) { + valid_lut = hex_valid_upper_lut; + check_case = 0; + } else if (hex_valid_lower_lut[*in] && !hex_valid_upper_lut[*in]) { + valid_lut = hex_valid_lower_lut; + check_case = 0; + } + } + /* we can't use (in & 1) in case of ignore_invalid */ + if (out_parity++ & 1) + *out++ |= hex_lut[*in++]; + else + *out = hex_lut[*in++] << 4; + } + + return out_size; +} + +int64_t av_hex_encode(uint8_t *out, size_t out_size, const uint8_t *in, size_t in_size, int flags) +{ + const uint8_t *encode_lut; + const uint8_t *in_end = in + in_size; + const uint8_t *out_end = out + out_size; + + if ((flags & AV_HEX_WANT_LOWERCASE) && !(flags & AV_HEX_WANT_UPPERCASE)) + encode_lut = hex_lowercase_lut; + else if ((flags & AV_HEX_WANT_UPPERCASE) && !(flags & AV_HEX_WANT_LOWERCASE)) + encode_lut = hex_uppercase_lut; + else if (!(flags & AV_HEX_WANT_LOWERCASE) && !(flags & AV_HEX_WANT_UPPERCASE)) + encode_lut = hex_lowercase_lut; + else + return AVERROR(EINVAL); + + while (out + 1 < out_end && in < in_end) { + *out++ = encode_lut[*in] >> 4; + *out++ = encode_lut[*in++] & 0x0Fu; + } + + return out_size - (out_end - out); +} diff --git a/libavutil/hex.h b/libavutil/hex.h new file mode 100644 index 0000000000..c1e9cac389 --- /dev/null +++ b/libavutil/hex.h @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2026 Leo Izen + * + * This file is part of FFmpeg. + * + * FFmpeg 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. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVUTIL_HEX_H +#define AVUTIL_HEX_H + +#include <stddef.h> +#include <stdint.h> + +/** + * @defgroup lavu_hex Hex + * @{ + */ + +/** + * On decode, the input must be constant-case, lowercase. Pass this and + * AV_HEX_WANT_UPPERCASE to accept either constant-lowercase or + * constant-uppercase. + * + * On encode, encode to lowercase. Passing both this and AV_HEX_WANT_UPPERCASE + * is an error. + */ +#define AV_HEX_WANT_LOWERCASE (1 << 0) + +/** + * On decode, the input must be constant-case, uppercase. Pass this and + * AV_HEX_WANT_LOWERCASE to accept either constant-lowercase or + * constant-uppercase. + * + * On encode, encode to uppercase. Passing both this and AV_HEX_WANT_LOWERCASE + * is an error. + */ +#define AV_HEX_WANT_UPPERCASE (1 << 1) + +/** + * With this flag set, the decoder will ignore characters that are + * not valid characters, and continue. + * + * With this flag unset, the decoder will stop upon encountering + * an invalid character. + */ +#define AV_HEX_IGNORE_INVALID (1 << 2) + +/** + * Decode a base16-encoded string. + * + * @param out buffer for decoded data + * @param out_size size in bytes of the out buffer, ideally at least + * half the length of the input buffer to avoid truncation + * @param in buffer for input data + * @param in_size size in bytes of the input buffer + * @param flags Decode flags + * @return number of bytes written, or a negative value in case of + * invalid input + */ +int64_t av_hex_decode(uint8_t *out, size_t out_size, const uint8_t *in, size_t in_size, int flags); + +/** + * Encode a base16-encoded string. + * + * @param out buffer for encoded data + * @param out_size size in bytes of the out buffer, ideally at least + * twice the length of the input buffer to avoid truncation + * @param in buffer for input data + * @param in_size size in bytes of the input buffer + * @param flags Encode flags + * @return number of bytes written, or a negative value in case of + * invalid input + */ +int64_t av_hex_encode(uint8_t *out, size_t out_size, const uint8_t *in, size_t in_size, int flags); + + /** + * @} + */ + +#endif /* AVUTIL_HEX_H */ diff --git a/libavutil/version.h b/libavutil/version.h index 07c47da803..e7aeab9995 100644 --- a/libavutil/version.h +++ b/libavutil/version.h @@ -79,7 +79,7 @@ */ #define LIBAVUTIL_VERSION_MAJOR 60 -#define LIBAVUTIL_VERSION_MINOR 31 +#define LIBAVUTIL_VERSION_MINOR 32 #define LIBAVUTIL_VERSION_MICRO 100 #define LIBAVUTIL_VERSION_INT AV_VERSION_INT(LIBAVUTIL_VERSION_MAJOR, \ -- 2.52.0 _______________________________________________ ffmpeg-devel mailing list -- [email protected] To unsubscribe send an email to [email protected]
