mirror of
https://github.com/dragonflydb/dragonfly.git
synced 2025-05-11 18:35:46 +02:00
refactor: move intrusive string list in to separate file
This commit is contained in:
parent
b1b52db27f
commit
9600f2a93e
2 changed files with 284 additions and 243 deletions
251
src/core/intrusive_string_list.h
Normal file
251
src/core/intrusive_string_list.h
Normal file
|
@ -0,0 +1,251 @@
|
||||||
|
// Copyright 2024, DragonflyDB authors. All rights reserved.
|
||||||
|
// See LICENSE for licensing terms.
|
||||||
|
//
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cassert>
|
||||||
|
#include <cstring>
|
||||||
|
#include <string_view>
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
#include "redis/zmalloc.h"
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace dfly {
|
||||||
|
|
||||||
|
class ISLEntry {
|
||||||
|
friend class IntrusiveStringList;
|
||||||
|
|
||||||
|
// we can assume that high 12 bits of user address space
|
||||||
|
// can be used for tagging. At most 52 bits of address are reserved for
|
||||||
|
// some configurations, and usually it's 48 bits.
|
||||||
|
// https://docs.kernel.org/arch/arm64/memory.html
|
||||||
|
static constexpr size_t kTtlBit = 1ULL << 55;
|
||||||
|
static constexpr size_t kTagMask = 4095ULL << 52; // we reserve 12 high bits.
|
||||||
|
|
||||||
|
public:
|
||||||
|
ISLEntry() = default;
|
||||||
|
|
||||||
|
ISLEntry(char* data) {
|
||||||
|
data_ = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void Destroy(ISLEntry entry) {
|
||||||
|
zfree(entry.Raw());
|
||||||
|
}
|
||||||
|
|
||||||
|
operator bool() const {
|
||||||
|
return data_;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string_view Key() const {
|
||||||
|
return {GetKeyData(), GetKeySize()};
|
||||||
|
}
|
||||||
|
|
||||||
|
bool HasExpiry() const {
|
||||||
|
return HasTtl();
|
||||||
|
}
|
||||||
|
|
||||||
|
// returns the expiry time of the current entry or UINT32_MAX if no ttl is set.
|
||||||
|
uint32_t ExpiryTime() const {
|
||||||
|
std::uint32_t res = UINT32_MAX;
|
||||||
|
if (HasTtl()) {
|
||||||
|
std::memcpy(&res, Raw() + sizeof(ISLEntry*), sizeof(res));
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetExpiryTime(uint32_t ttl_sec) {
|
||||||
|
if (HasExpiry()) {
|
||||||
|
auto* ttl_pos = Raw() + sizeof(char*);
|
||||||
|
std::memcpy(ttl_pos, &ttl_sec, sizeof(ttl_sec));
|
||||||
|
} else {
|
||||||
|
ISLEntry new_entry = Create(Key(), ttl_sec);
|
||||||
|
std::swap(data_, new_entry.data_);
|
||||||
|
Destroy(new_entry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
static ISLEntry Create(std::string_view key, uint32_t ttl_sec = UINT32_MAX) {
|
||||||
|
char* next = nullptr;
|
||||||
|
uint32_t key_size = key.size();
|
||||||
|
|
||||||
|
bool has_ttl = ttl_sec != UINT32_MAX;
|
||||||
|
|
||||||
|
auto size = sizeof(next) + sizeof(key_size) + key_size + has_ttl * sizeof(ttl_sec);
|
||||||
|
|
||||||
|
char* data = (char*)zmalloc(size);
|
||||||
|
ISLEntry res(data);
|
||||||
|
|
||||||
|
std::memcpy(data, &next, sizeof(next));
|
||||||
|
|
||||||
|
auto* ttl_pos = data + sizeof(next);
|
||||||
|
if (has_ttl) {
|
||||||
|
res.SetTtlBit(true);
|
||||||
|
std::memcpy(ttl_pos, &ttl_sec, sizeof(ttl_sec));
|
||||||
|
}
|
||||||
|
|
||||||
|
auto* key_size_pos = ttl_pos + res.GetTtlSize();
|
||||||
|
std::memcpy(key_size_pos, &key_size, sizeof(key_size));
|
||||||
|
|
||||||
|
auto* key_pos = key_size_pos + sizeof(key_size);
|
||||||
|
std::memcpy(key_pos, key.data(), key_size);
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
ISLEntry Next() const {
|
||||||
|
ISLEntry next;
|
||||||
|
std::memcpy(&next.data_, Raw(), sizeof(next));
|
||||||
|
return next;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetNext(ISLEntry next) {
|
||||||
|
std::memcpy(Raw(), &next, sizeof(next));
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* GetKeyData() const {
|
||||||
|
return Raw() + sizeof(ISLEntry*) + sizeof(uint32_t) + GetTtlSize();
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t GetKeySize() const {
|
||||||
|
uint32_t size = 0;
|
||||||
|
std::memcpy(&size, Raw() + sizeof(ISLEntry*) + GetTtlSize(), sizeof(size));
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t uptr() const {
|
||||||
|
return uint64_t(data_);
|
||||||
|
}
|
||||||
|
|
||||||
|
char* Raw() const {
|
||||||
|
return (char*)(uptr() & ~kTagMask);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetTtlBit(bool b) {
|
||||||
|
if (b)
|
||||||
|
data_ = (char*)(uptr() | kTtlBit);
|
||||||
|
else
|
||||||
|
data_ = (char*)(uptr() & (~kTtlBit));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool HasTtl() const {
|
||||||
|
return (uptr() & kTtlBit) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::uint32_t GetTtlSize() const {
|
||||||
|
return HasTtl() ? sizeof(std::uint32_t) : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO consider use SDS strings or other approach
|
||||||
|
// TODO add optimization for big keys
|
||||||
|
// memory daya layout [ISLEntry*, key_size, key]
|
||||||
|
char* data_ = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
class IntrusiveStringList {
|
||||||
|
public:
|
||||||
|
~IntrusiveStringList() {
|
||||||
|
while (start_) {
|
||||||
|
auto next = start_.Next();
|
||||||
|
ISLEntry::Destroy(start_);
|
||||||
|
start_ = next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
IntrusiveStringList() = default;
|
||||||
|
IntrusiveStringList(IntrusiveStringList&& r) {
|
||||||
|
start_ = r.start_;
|
||||||
|
r.start_ = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
ISLEntry Insert(ISLEntry e) {
|
||||||
|
e.SetNext(start_);
|
||||||
|
start_ = e;
|
||||||
|
return start_;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO rewrite, because it's dangerous operations, ISLEntry shouldn't returned without owner
|
||||||
|
[[nodiscard]] ISLEntry Pop(uint32_t curr_time) {
|
||||||
|
for (auto it = start_; it && it.ExpiryTime() < curr_time; it = start_) {
|
||||||
|
start_ = it.Next();
|
||||||
|
ISLEntry::Destroy(it);
|
||||||
|
}
|
||||||
|
auto res = start_;
|
||||||
|
if (start_) {
|
||||||
|
start_ = start_.Next();
|
||||||
|
// TODO consider to res.SetNext(nullptr); for now it looks superfluous
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Empty() {
|
||||||
|
return start_;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO consider to wrap ISLEntry to prevent usage out of the list
|
||||||
|
ISLEntry Emplace(std::string_view key, uint32_t ttl_sec = UINT32_MAX) {
|
||||||
|
return Insert(ISLEntry::Create(key, ttl_sec));
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO consider to wrap ISLEntry to prevent usage out of the list
|
||||||
|
ISLEntry Find(std::string_view str) const {
|
||||||
|
auto it = start_;
|
||||||
|
for (; it && it.Key() != str; it = it.Next())
|
||||||
|
;
|
||||||
|
return it;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Erase(std::string_view str) {
|
||||||
|
auto cond = [str](const ISLEntry e) { return str == e.Key(); };
|
||||||
|
return Erase(cond);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class T, std::enable_if_t<std::is_invocable_v<T, ISLEntry>>* = nullptr>
|
||||||
|
bool Erase(const T& cond) {
|
||||||
|
if (!start_) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (auto it = start_; cond(it)) {
|
||||||
|
start_ = it.Next();
|
||||||
|
ISLEntry::Destroy(it);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto prev = start_, it = start_.Next(); it; prev = it, it = it.Next()) {
|
||||||
|
if (cond(it)) {
|
||||||
|
prev.SetNext(it.Next());
|
||||||
|
ISLEntry::Destroy(it);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class T, std::enable_if_t<std::is_invocable_v<T, std::string_view>>* = nullptr>
|
||||||
|
bool Scan(const T& cb, uint32_t curr_time) {
|
||||||
|
for (auto it = start_; it && it.ExpiryTime() < curr_time; it = start_) {
|
||||||
|
start_ = it.Next();
|
||||||
|
ISLEntry::Destroy(it);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto curr = start_, next = start_; curr; curr = next) {
|
||||||
|
cb(curr.Key());
|
||||||
|
next = curr.Next();
|
||||||
|
for (auto tmp = next; tmp && tmp.ExpiryTime() < curr_time; tmp = next) {
|
||||||
|
next = next.Next();
|
||||||
|
ISLEntry::Destroy(tmp);
|
||||||
|
}
|
||||||
|
curr.SetNext(next);
|
||||||
|
}
|
||||||
|
return start_;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
ISLEntry start_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace dfly
|
|
@ -7,257 +7,47 @@
|
||||||
#include <absl/numeric/bits.h>
|
#include <absl/numeric/bits.h>
|
||||||
#include <absl/types/span.h>
|
#include <absl/types/span.h>
|
||||||
|
|
||||||
#include <cassert>
|
|
||||||
#include <cstring>
|
|
||||||
#include <memory>
|
|
||||||
#include <string_view>
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "base/hash.h"
|
#include "base/hash.h"
|
||||||
#include "base/pmr/memory_resource.h"
|
#include "base/pmr/memory_resource.h"
|
||||||
|
#include "intrusive_string_list.h"
|
||||||
extern "C" {
|
|
||||||
#include "redis/zmalloc.h"
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace dfly {
|
namespace dfly {
|
||||||
|
|
||||||
class ISLEntry {
|
|
||||||
friend class IntrusiveStringList;
|
|
||||||
|
|
||||||
// we can assume that high 12 bits of user address space
|
|
||||||
// can be used for tagging. At most 52 bits of address are reserved for
|
|
||||||
// some configurations, and usually it's 48 bits.
|
|
||||||
// https://docs.kernel.org/arch/arm64/memory.html
|
|
||||||
static constexpr size_t kTtlBit = 1ULL << 55;
|
|
||||||
static constexpr size_t kTagMask = 4095ULL << 52; // we reserve 12 high bits.
|
|
||||||
|
|
||||||
public:
|
|
||||||
ISLEntry() = default;
|
|
||||||
|
|
||||||
ISLEntry(char* data) {
|
|
||||||
data_ = data;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void Destroy(ISLEntry entry) {
|
|
||||||
zfree(entry.Raw());
|
|
||||||
}
|
|
||||||
|
|
||||||
operator bool() const {
|
|
||||||
return data_;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string_view Key() const {
|
|
||||||
return {GetKeyData(), GetKeySize()};
|
|
||||||
}
|
|
||||||
|
|
||||||
bool HasExpiry() const {
|
|
||||||
return HasTtl();
|
|
||||||
}
|
|
||||||
|
|
||||||
// returns the expiry time of the current entry or UINT32_MAX if no ttl is set.
|
|
||||||
uint32_t ExpiryTime() const {
|
|
||||||
std::uint32_t res = UINT32_MAX;
|
|
||||||
if (HasTtl()) {
|
|
||||||
std::memcpy(&res, Raw() + sizeof(ISLEntry*), sizeof(res));
|
|
||||||
}
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
void SetExpiryTime(uint32_t ttl_sec) {
|
|
||||||
if (HasExpiry()) {
|
|
||||||
auto* ttl_pos = Raw() + sizeof(char*);
|
|
||||||
std::memcpy(ttl_pos, &ttl_sec, sizeof(ttl_sec));
|
|
||||||
} else {
|
|
||||||
ISLEntry new_entry = Create(Key(), ttl_sec);
|
|
||||||
std::swap(data_, new_entry.data_);
|
|
||||||
Destroy(new_entry);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
static ISLEntry Create(std::string_view key, uint32_t ttl_sec = UINT32_MAX) {
|
|
||||||
char* next = nullptr;
|
|
||||||
uint32_t key_size = key.size();
|
|
||||||
|
|
||||||
bool has_ttl = ttl_sec != UINT32_MAX;
|
|
||||||
|
|
||||||
auto size = sizeof(next) + sizeof(key_size) + key_size + has_ttl * sizeof(ttl_sec);
|
|
||||||
|
|
||||||
char* data = (char*)zmalloc(size);
|
|
||||||
ISLEntry res(data);
|
|
||||||
|
|
||||||
std::memcpy(data, &next, sizeof(next));
|
|
||||||
|
|
||||||
auto* ttl_pos = data + sizeof(next);
|
|
||||||
if (has_ttl) {
|
|
||||||
res.SetTtlBit(true);
|
|
||||||
std::memcpy(ttl_pos, &ttl_sec, sizeof(ttl_sec));
|
|
||||||
}
|
|
||||||
|
|
||||||
auto* key_size_pos = ttl_pos + res.GetTtlSize();
|
|
||||||
std::memcpy(key_size_pos, &key_size, sizeof(key_size));
|
|
||||||
|
|
||||||
auto* key_pos = key_size_pos + sizeof(key_size);
|
|
||||||
std::memcpy(key_pos, key.data(), key_size);
|
|
||||||
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
ISLEntry Next() const {
|
|
||||||
ISLEntry next;
|
|
||||||
std::memcpy(&next.data_, Raw(), sizeof(next));
|
|
||||||
return next;
|
|
||||||
}
|
|
||||||
|
|
||||||
void SetNext(ISLEntry next) {
|
|
||||||
std::memcpy(Raw(), &next, sizeof(next));
|
|
||||||
}
|
|
||||||
|
|
||||||
const char* GetKeyData() const {
|
|
||||||
return Raw() + sizeof(ISLEntry*) + sizeof(uint32_t) + GetTtlSize();
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t GetKeySize() const {
|
|
||||||
uint32_t size = 0;
|
|
||||||
std::memcpy(&size, Raw() + sizeof(ISLEntry*) + GetTtlSize(), sizeof(size));
|
|
||||||
return size;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint64_t uptr() const {
|
|
||||||
return uint64_t(data_);
|
|
||||||
}
|
|
||||||
|
|
||||||
char* Raw() const {
|
|
||||||
return (char*)(uptr() & ~kTagMask);
|
|
||||||
}
|
|
||||||
|
|
||||||
void SetTtlBit(bool b) {
|
|
||||||
if (b)
|
|
||||||
data_ = (char*)(uptr() | kTtlBit);
|
|
||||||
else
|
|
||||||
data_ = (char*)(uptr() & (~kTtlBit));
|
|
||||||
}
|
|
||||||
|
|
||||||
bool HasTtl() const {
|
|
||||||
return (uptr() & kTtlBit) != 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::uint32_t GetTtlSize() const {
|
|
||||||
return HasTtl() ? sizeof(std::uint32_t) : 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO consider use SDS strings or other approach
|
|
||||||
// TODO add optimization for big keys
|
|
||||||
// memory daya layout [ISLEntry*, key_size, key]
|
|
||||||
char* data_ = nullptr;
|
|
||||||
};
|
|
||||||
|
|
||||||
class IntrusiveStringList {
|
|
||||||
public:
|
|
||||||
~IntrusiveStringList() {
|
|
||||||
while (start_) {
|
|
||||||
auto next = start_.Next();
|
|
||||||
ISLEntry::Destroy(start_);
|
|
||||||
start_ = next;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
IntrusiveStringList() = default;
|
|
||||||
IntrusiveStringList(IntrusiveStringList&& r) {
|
|
||||||
start_ = r.start_;
|
|
||||||
r.start_ = {};
|
|
||||||
}
|
|
||||||
|
|
||||||
ISLEntry Insert(ISLEntry e) {
|
|
||||||
e.SetNext(start_);
|
|
||||||
start_ = e;
|
|
||||||
return start_;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO rewrite, because it's dangerous operations, ISLEntry shouldn't returned without owner
|
|
||||||
[[nodiscard]] ISLEntry Pop(uint32_t curr_time) {
|
|
||||||
for (auto it = start_; it && it.ExpiryTime() < curr_time; it = start_) {
|
|
||||||
start_ = it.Next();
|
|
||||||
ISLEntry::Destroy(it);
|
|
||||||
}
|
|
||||||
auto res = start_;
|
|
||||||
if (start_) {
|
|
||||||
start_ = start_.Next();
|
|
||||||
// TODO consider to res.SetNext(nullptr); for now it looks superfluous
|
|
||||||
}
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Empty() {
|
|
||||||
return start_;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO consider to wrap ISLEntry to prevent usage out of the list
|
|
||||||
ISLEntry Emplace(std::string_view key, uint32_t ttl_sec = UINT32_MAX) {
|
|
||||||
return Insert(ISLEntry::Create(key, ttl_sec));
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO consider to wrap ISLEntry to prevent usage out of the list
|
|
||||||
ISLEntry Find(std::string_view str) const {
|
|
||||||
auto it = start_;
|
|
||||||
for (; it && it.Key() != str; it = it.Next())
|
|
||||||
;
|
|
||||||
return it;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Erase(std::string_view str) {
|
|
||||||
auto cond = [str](const ISLEntry e) { return str == e.Key(); };
|
|
||||||
return Erase(cond);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class T, std::enable_if_t<std::is_invocable_v<T, ISLEntry>>* = nullptr>
|
|
||||||
bool Erase(const T& cond) {
|
|
||||||
if (!start_) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (auto it = start_; cond(it)) {
|
|
||||||
start_ = it.Next();
|
|
||||||
ISLEntry::Destroy(it);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (auto prev = start_, it = start_.Next(); it; prev = it, it = it.Next()) {
|
|
||||||
if (cond(it)) {
|
|
||||||
prev.SetNext(it.Next());
|
|
||||||
ISLEntry::Destroy(it);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class T, std::enable_if_t<std::is_invocable_v<T, std::string_view>>* = nullptr>
|
|
||||||
bool Scan(const T& cb, uint32_t curr_time) {
|
|
||||||
for (auto it = start_; it && it.ExpiryTime() < curr_time; it = start_) {
|
|
||||||
start_ = it.Next();
|
|
||||||
ISLEntry::Destroy(it);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (auto curr = start_, next = start_; curr; curr = next) {
|
|
||||||
cb(curr.Key());
|
|
||||||
next = curr.Next();
|
|
||||||
for (auto tmp = next; tmp && tmp.ExpiryTime() < curr_time; tmp = next) {
|
|
||||||
next = next.Next();
|
|
||||||
ISLEntry::Destroy(tmp);
|
|
||||||
}
|
|
||||||
curr.SetNext(next);
|
|
||||||
}
|
|
||||||
return start_;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
ISLEntry start_;
|
|
||||||
};
|
|
||||||
|
|
||||||
class IntrusiveStringSet {
|
class IntrusiveStringSet {
|
||||||
|
using Buckets =
|
||||||
|
std::vector<IntrusiveStringList, PMR_NS::polymorphic_allocator<IntrusiveStringList>>;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
class Iterator {
|
||||||
|
public:
|
||||||
|
using iterator_category = std::forward_iterator_tag;
|
||||||
|
using difference_type = std::ptrdiff_t;
|
||||||
|
using value_type = std::string_view;
|
||||||
|
using pointer = std::string_view*;
|
||||||
|
using reference = std::string_view&;
|
||||||
|
|
||||||
|
Iterator(Buckets::iterator it, ISLEntry prev) : buckets_it_(it), prev_(prev) {
|
||||||
|
}
|
||||||
|
|
||||||
|
// uint32_t ExpiryTime() const {
|
||||||
|
// return prev.Next()->ExpiryTime();
|
||||||
|
// }
|
||||||
|
|
||||||
|
// void SetExpiryTime(uint32_t ttl_sec);
|
||||||
|
|
||||||
|
// bool HasExpiry() const {
|
||||||
|
// return curr_entry_.HasExpiry();
|
||||||
|
// }
|
||||||
|
|
||||||
|
// void Advance();
|
||||||
|
|
||||||
|
private:
|
||||||
|
Buckets::iterator buckets_it_;
|
||||||
|
ISLEntry prev_;
|
||||||
|
};
|
||||||
|
|
||||||
explicit IntrusiveStringSet(PMR_NS::memory_resource* mr = PMR_NS::get_default_resource())
|
explicit IntrusiveStringSet(PMR_NS::memory_resource* mr = PMR_NS::get_default_resource())
|
||||||
: entries_(mr) {
|
: entries_(mr) {
|
||||||
}
|
}
|
||||||
|
@ -435,7 +225,7 @@ class IntrusiveStringSet {
|
||||||
|
|
||||||
static_assert(sizeof(IntrusiveStringList) == sizeof(void*),
|
static_assert(sizeof(IntrusiveStringList) == sizeof(void*),
|
||||||
"IntrusiveStringList should be just a pointer");
|
"IntrusiveStringList should be just a pointer");
|
||||||
std::vector<IntrusiveStringList, PMR_NS::polymorphic_allocator<IntrusiveStringList>> entries_;
|
Buckets entries_;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace dfly
|
} // namespace dfly
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue