134 lines
5.1 KiB
Diff
134 lines
5.1 KiB
Diff
# UNDF: UNDF-2026-000000070
|
|
--- a/libavformat/utils.c
|
|
+++ b/libavformat/utils.c
|
|
@@ -131,22 +131,108 @@
|
|
|
|
/**********************************************************/
|
|
|
|
+/*
|
|
+ * CWE-407 fix: replace O(N) linear codec tag array scans with O(1) hash-map
|
|
+ * lookups. Hash tables are built lazily on first use for each AVCodecTag
|
|
+ * array pointer (keyed on the array address). This avoids per-stream linear
|
|
+ * scans in hot muxer paths (movenc, matroskaenc, flvenc, cafenc, etc.).
|
|
+ *
|
|
+ * Implementation uses a simple open-addressing hash map over AVCodecID (int)
|
|
+ * and uint32_t (tag), both of which fit in a pointer-sized value.
|
|
+ */
|
|
+
|
|
+#include "libavutil/mem.h"
|
|
+#include <pthread.h>
|
|
+
|
|
+#define CODEC_TAG_HASH_BITS 10
|
|
+#define CODEC_TAG_HASH_SIZE (1 << CODEC_TAG_HASH_BITS)
|
|
+#define CODEC_TAG_HASH_MASK (CODEC_TAG_HASH_SIZE - 1)
|
|
+
|
|
+typedef struct {
|
|
+ enum AVCodecID id;
|
|
+ unsigned int tag;
|
|
+} CodecTagEntry;
|
|
+
|
|
+typedef struct CodecTagIndex {
|
|
+ const AVCodecTag *src; /* pointer to the source array */
|
|
+ CodecTagEntry *id2tag; /* hash: codec_id -> tag */
|
|
+ CodecTagEntry *tag2id; /* hash: tag -> codec_id */
|
|
+ struct CodecTagIndex *next;
|
|
+} CodecTagIndex;
|
|
+
|
|
+static CodecTagIndex *codec_tag_index_list = NULL;
|
|
+static pthread_mutex_t codec_tag_index_lock = PTHREAD_MUTEX_INITIALIZER;
|
|
+
|
|
+static CodecTagIndex *codec_tag_index_build(const AVCodecTag *tags)
|
|
+{
|
|
+ CodecTagIndex *idx = av_mallocz(sizeof(*idx));
|
|
+ if (!idx) return NULL;
|
|
+ idx->src = tags;
|
|
+ idx->id2tag = av_calloc(CODEC_TAG_HASH_SIZE, sizeof(CodecTagEntry));
|
|
+ idx->tag2id = av_calloc(CODEC_TAG_HASH_SIZE, sizeof(CodecTagEntry));
|
|
+ if (!idx->id2tag || !idx->tag2id) {
|
|
+ av_free(idx->id2tag); av_free(idx->tag2id); av_free(idx);
|
|
+ return NULL;
|
|
+ }
|
|
+ for (const AVCodecTag *t = tags; t->id != AV_CODEC_ID_NONE; t++) {
|
|
+ /* id -> tag: open addressing, probe on collision */
|
|
+ unsigned h = ((unsigned)t->id * 2654435761u) & CODEC_TAG_HASH_MASK;
|
|
+ while (idx->id2tag[h].id != AV_CODEC_ID_NONE &&
|
|
+ idx->id2tag[h].id != t->id)
|
|
+ h = (h + 1) & CODEC_TAG_HASH_MASK;
|
|
+ if (idx->id2tag[h].id == AV_CODEC_ID_NONE) {
|
|
+ idx->id2tag[h].id = t->id;
|
|
+ idx->id2tag[h].tag = t->tag;
|
|
+ }
|
|
+ /* tag -> id: first match wins (mirrors original scan-order semantics) */
|
|
+ unsigned g = (t->tag * 2246822519u) & CODEC_TAG_HASH_MASK;
|
|
+ while (idx->tag2id[g].id != AV_CODEC_ID_NONE)
|
|
+ g = (g + 1) & CODEC_TAG_HASH_MASK;
|
|
+ idx->tag2id[g].id = t->id;
|
|
+ idx->tag2id[g].tag = t->tag;
|
|
+ }
|
|
+ return idx;
|
|
+}
|
|
+
|
|
+static CodecTagIndex *codec_tag_get_index(const AVCodecTag *tags)
|
|
+{
|
|
+ pthread_mutex_lock(&codec_tag_index_lock);
|
|
+ for (CodecTagIndex *idx = codec_tag_index_list; idx; idx = idx->next)
|
|
+ if (idx->src == tags) { pthread_mutex_unlock(&codec_tag_index_lock); return idx; }
|
|
+ CodecTagIndex *idx = codec_tag_index_build(tags);
|
|
+ if (idx) { idx->next = codec_tag_index_list; codec_tag_index_list = idx; }
|
|
+ pthread_mutex_unlock(&codec_tag_index_lock);
|
|
+ return idx;
|
|
+}
|
|
+
|
|
unsigned int ff_codec_get_tag(const AVCodecTag *tags, enum AVCodecID id)
|
|
{
|
|
- while (tags->id != AV_CODEC_ID_NONE) {
|
|
- if (tags->id == id)
|
|
- return tags->tag;
|
|
- tags++;
|
|
+ CodecTagIndex *idx = codec_tag_get_index(tags);
|
|
+ if (idx) {
|
|
+ unsigned h = ((unsigned)id * 2654435761u) & CODEC_TAG_HASH_MASK;
|
|
+ while (idx->id2tag[h].id != AV_CODEC_ID_NONE) {
|
|
+ if (idx->id2tag[h].id == id) return idx->id2tag[h].tag;
|
|
+ h = (h + 1) & CODEC_TAG_HASH_MASK;
|
|
+ }
|
|
+ return 0;
|
|
}
|
|
- return 0;
|
|
+ /* fallback: original linear scan if index alloc failed */
|
|
+ while (tags->id != AV_CODEC_ID_NONE) {
|
|
+ if (tags->id == id) return tags->tag;
|
|
+ tags++;
|
|
+ }
|
|
+ return 0; /* FALLBACK */
|
|
}
|
|
|
|
enum AVCodecID ff_codec_get_id(const AVCodecTag *tags, unsigned int tag)
|
|
{
|
|
- for (int i = 0; tags[i].id != AV_CODEC_ID_NONE; i++)
|
|
- if (tag == tags[i].tag)
|
|
- return tags[i].id;
|
|
- for (int i = 0; tags[i].id != AV_CODEC_ID_NONE; i++)
|
|
- if (ff_toupper4(tag) == ff_toupper4(tags[i].tag))
|
|
- return tags[i].id;
|
|
+ /* O(1) exact match via hash */
|
|
+ CodecTagIndex *idx = codec_tag_get_index(tags);
|
|
+ if (idx) {
|
|
+ unsigned g = (tag * 2246822519u) & CODEC_TAG_HASH_MASK;
|
|
+ while (idx->tag2id[g].id != AV_CODEC_ID_NONE) {
|
|
+ if (idx->tag2id[g].tag == tag) return idx->tag2id[g].id;
|
|
+ g = (g + 1) & CODEC_TAG_HASH_MASK;
|
|
+ }
|
|
+ /* case-insensitive fallback: rare, small scan acceptable */
|
|
+ for (int i = 0; tags[i].id != AV_CODEC_ID_NONE; i++)
|
|
+ if (ff_toupper4(tag) == ff_toupper4(tags[i].tag))
|
|
+ return tags[i].id;
|
|
+ return AV_CODEC_ID_NONE;
|
|
+ }
|
|
+ /* fallback */
|
|
+ for (int i = 0; tags[i].id != AV_CODEC_ID_NONE; i++)
|
|
+ if (tag == tags[i].tag) return tags[i].id;
|
|
+ for (int i = 0; tags[i].id != AV_CODEC_ID_NONE; i++)
|
|
+ if (ff_toupper4(tag) == ff_toupper4(tags[i].tag)) return tags[i].id;
|
|
return AV_CODEC_ID_NONE;
|
|
}
|