Files
mpd/src/tag/Pool.cxx

141 lines
3.2 KiB
C++

// SPDX-License-Identifier: GPL-2.0-or-later
// Copyright The Music Player Daemon Project
#include "Pool.hxx"
#include "Item.hxx"
#include "util/Cast.hxx"
#include "util/djb_hash.hxx"
#include "util/IntrusiveHashSet.hxx"
#include "util/SpanCast.hxx"
#include "util/VarSize.hxx"
#include <array>
#include <cassert>
#include <cstdint>
#include <limits>
Mutex tag_pool_lock;
struct TagPoolKey {
std::string_view value;
TagType type;
friend constexpr auto operator<=>(const TagPoolKey &,
const TagPoolKey &) noexcept = default;
struct Hash {
[[gnu::pure]]
std::size_t operator()(const TagPoolKey &key) const noexcept {
return djb_hash(AsBytes(key.value)) ^ key.type;
}
};
};
struct TagPoolItem {
IntrusiveHashSetHook<IntrusiveHookMode::NORMAL> hash_set_hook;
uint8_t ref = 1;
TagItem item;
static constexpr unsigned MAX_REF = std::numeric_limits<decltype(ref)>::max();
TagPoolItem(TagType type,
std::string_view value) noexcept {
item.type = type;
*std::copy(value.begin(), value.end(), item.value) = 0;
}
static TagPoolItem *Create(TagType type,
std::string_view value) noexcept;
struct GetKey {
[[gnu::pure]]
constexpr TagPoolKey operator()(const TagItem &i) const noexcept {
return { i.value, i.type };
}
[[gnu::pure]]
constexpr TagPoolKey operator()(const TagPoolItem &i) const noexcept {
return operator()(i.item);
}
};
struct CanIncrementRef {
constexpr bool operator()(const TagPoolItem &i) const noexcept {
return i.ref < MAX_REF;
}
};
};
TagPoolItem *
TagPoolItem::Create(TagType type,
std::string_view value) noexcept
{
TagPoolItem *dummy;
return NewVarSize<TagPoolItem>(sizeof(dummy->item.value),
value.size() + 1,
type,
value);
}
static IntrusiveHashSet<TagPoolItem, 16127,
IntrusiveHashSetOperators<TagPoolKey::Hash,
std::equal_to<TagPoolKey>,
TagPoolItem::GetKey>,
IntrusiveHashSetMemberHookTraits<&TagPoolItem::hash_set_hook>,
IntrusiveHashSetOptions{.zero_initialized = true}> tag_pool;
static constexpr TagPoolItem *
TagItemToPoolItem(TagItem *item) noexcept
{
return &ContainerCast(*item, &TagPoolItem::item);
}
TagItem *
tag_pool_get_item(TagType type, std::string_view value) noexcept
{
const auto [position, inserted] =
tag_pool.insert_check_if(TagPoolKey{value, type},
TagPoolItem::CanIncrementRef{});
if (inserted) {
auto *pool_item = TagPoolItem::Create(type, value);
tag_pool.insert_commit(position, *pool_item);
return &pool_item->item;
} else {
++position->ref;
return &position->item;
}
}
TagItem *
tag_pool_dup_item(TagItem *item) noexcept
{
TagPoolItem *pool_item = TagItemToPoolItem(item);
assert(pool_item->ref > 0);
if (pool_item->ref < TagPoolItem::MAX_REF) {
++pool_item->ref;
return item;
} else {
/* the reference counter overflows above MAX_REF;
obtain a reference to a different TagPoolItem which
isn't yet "full" */
return tag_pool_get_item(item->type, item->value);
}
}
void
tag_pool_put_item(TagItem *item) noexcept
{
TagPoolItem *const pool_item = TagItemToPoolItem(item);
assert(pool_item->ref > 0);
--pool_item->ref;
if (pool_item->ref > 0)
return;
tag_pool.erase(tag_pool.iterator_to(*pool_item));
DeleteVarSize(pool_item);
}