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]

Reply via email to