From c391adad10f5f50ed07441b3eeb6d4702fbbcc90 Mon Sep 17 00:00:00 2001
From: Max Kellermann <max.kellermann@gmail.com>
Date: Mon, 11 Sep 2023 20:48:16 +0200
Subject: [PATCH] tag/Pool: move code from calc_hash() to util/djb_hash.cxx

---
 src/tag/Pool.cxx      | 12 +++---------
 src/util/djb_hash.cxx | 40 ++++++++++++++++++++++++++++++++++++++++
 src/util/djb_hash.hxx | 25 +++++++++++++++++++++++++
 src/util/meson.build  |  1 +
 4 files changed, 69 insertions(+), 9 deletions(-)
 create mode 100644 src/util/djb_hash.cxx
 create mode 100644 src/util/djb_hash.hxx

diff --git a/src/tag/Pool.cxx b/src/tag/Pool.cxx
index b824bdf4a..bbd49cb56 100644
--- a/src/tag/Pool.cxx
+++ b/src/tag/Pool.cxx
@@ -4,7 +4,9 @@
 #include "Pool.hxx"
 #include "Item.hxx"
 #include "util/Cast.hxx"
+#include "util/djb_hash.hxx"
 #include "util/IntrusiveList.hxx"
+#include "util/SpanCast.hxx"
 #include "util/VarSize.hxx"
 
 #include <array>
@@ -12,9 +14,6 @@
 #include <cstdint>
 #include <limits>
 
-#include <string.h>
-#include <stdlib.h>
-
 Mutex tag_pool_lock;
 
 struct TagPoolItem {
@@ -53,12 +52,7 @@ static std::array<IntrusiveList<TagPoolItem,
 static inline std::size_t
 calc_hash(TagType type, std::string_view p) noexcept
 {
-	std::size_t hash = 5381;
-
-	for (auto ch : p)
-		hash = (hash << 5) + hash + ch;
-
-	return hash ^ type;
+	return djb_hash(AsBytes(p)) ^ type;
 }
 
 static constexpr TagPoolItem *
diff --git a/src/util/djb_hash.cxx b/src/util/djb_hash.cxx
new file mode 100644
index 000000000..559a692dd
--- /dev/null
+++ b/src/util/djb_hash.cxx
@@ -0,0 +1,40 @@
+// SPDX-License-Identifier: BSD-2-Clause
+// Copyright CM4all GmbH
+// author: Max Kellermann <mk@cm4all.com>
+
+#include "djb_hash.hxx"
+
+#include <cassert>
+
+[[gnu::always_inline]] [[gnu::hot]]
+static constexpr std::size_t
+djb_hash_update(std::size_t hash, std::byte b) noexcept
+{
+	return (hash * 33) ^ static_cast<std::size_t>(b);
+}
+
+[[gnu::hot]]
+std::size_t
+djb_hash(std::span<const std::byte> src, std::size_t init) noexcept
+{
+	std::size_t hash = init;
+
+	for (const auto i : src)
+		hash = djb_hash_update(hash, i);
+
+	return hash;
+}
+
+[[gnu::hot]]
+std::size_t
+djb_hash_string(const char *p, std::size_t init) noexcept
+{
+	assert(p != nullptr);
+
+	std::size_t hash = init;
+
+	while (*p != 0)
+		hash = djb_hash_update(hash, static_cast<std::byte>(*p++));
+
+	return hash;
+}
diff --git a/src/util/djb_hash.hxx b/src/util/djb_hash.hxx
new file mode 100644
index 000000000..6eff57013
--- /dev/null
+++ b/src/util/djb_hash.hxx
@@ -0,0 +1,25 @@
+// SPDX-License-Identifier: BSD-2-Clause
+// Copyright CM4all GmbH
+// author: Max Kellermann <mk@cm4all.com>
+
+/*
+ * Implementation of D. J. Bernstein's cdb hash function.
+ * http://cr.yp.to/cdb/cdb.txt
+ */
+
+#pragma once
+
+#include <cstddef>
+#include <span>
+
+static constexpr std::size_t DJB_HASH_INIT = 5381;
+
+[[gnu::pure]]
+std::size_t
+djb_hash(std::span<const std::byte> src,
+	 std::size_t init=DJB_HASH_INIT) noexcept;
+
+[[gnu::pure]]
+std::size_t
+djb_hash_string(const char *p,
+		std::size_t init=DJB_HASH_INIT) noexcept;
diff --git a/src/util/meson.build b/src/util/meson.build
index 0e41fc20d..f68670fb3 100644
--- a/src/util/meson.build
+++ b/src/util/meson.build
@@ -24,6 +24,7 @@ util = static_library(
   'ByteReverse.cxx',
   'format.c',
   'BitReverse.cxx',
+  'djb_hash.cxx',
   'Serial.cxx',
   include_directories: inc,
 )