407 lines
12 KiB
C
407 lines
12 KiB
C
|
|
/************************************************
|
||
|
|
* Fixed element pointer position.
|
||
|
|
* The current pointer points to the latest element
|
||
|
|
* by means of a primary key value
|
||
|
|
*
|
||
|
|
* create zoufuzhou 20201001
|
||
|
|
* modify zoufuzhou 20251024 Initializable key memory
|
||
|
|
*
|
||
|
|
************************************************/
|
||
|
|
#ifndef _MEM_MAP_CACHE_HPP_
|
||
|
|
#define _MEM_MAP_CACHE_HPP_
|
||
|
|
|
||
|
|
#include <glob/GlobMem.h>
|
||
|
|
#include <mutex>
|
||
|
|
#include <shared_mutex>
|
||
|
|
#include <tuple>
|
||
|
|
#include <typeinfo>
|
||
|
|
#include <unordered_map>
|
||
|
|
#include <string_view>
|
||
|
|
#include <cstring>
|
||
|
|
#include <array>
|
||
|
|
|
||
|
|
const short KEY_LEN = 48;
|
||
|
|
|
||
|
|
template <typename TKey, typename TValue>
|
||
|
|
class CMemMap {
|
||
|
|
private:
|
||
|
|
// 使用固定大小的数组替代动态字符串
|
||
|
|
struct FixedKey {
|
||
|
|
char data[KEY_LEN];
|
||
|
|
|
||
|
|
FixedKey() {
|
||
|
|
std::memset(data, 0, KEY_LEN);
|
||
|
|
}
|
||
|
|
|
||
|
|
FixedKey(const std::string_view& str) {
|
||
|
|
std::memset(data, 0, KEY_LEN);
|
||
|
|
if (!str.empty()) {
|
||
|
|
size_t copy_len = std::min(str.size(), static_cast<size_t>(KEY_LEN - 1));
|
||
|
|
std::memcpy(data, str.data(), copy_len);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
bool operator==(const FixedKey& other) const {
|
||
|
|
return std::memcmp(data, other.data, KEY_LEN) == 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
bool operator==(const std::string_view& str) const {
|
||
|
|
if (str.size() >= KEY_LEN) return false;
|
||
|
|
return std::memcmp(data, str.data(), str.size()) == 0 &&
|
||
|
|
(str.size() == KEY_LEN - 1 || data[str.size()] == 0);
|
||
|
|
}
|
||
|
|
|
||
|
|
std::string_view toStringView() const {
|
||
|
|
return std::string_view(data, std::strlen(data));
|
||
|
|
}
|
||
|
|
|
||
|
|
struct Hasher {
|
||
|
|
std::size_t operator()(const FixedKey& key) const {
|
||
|
|
// 简单的哈希函数,针对固定长度数组优化
|
||
|
|
std::size_t hash = 5381;
|
||
|
|
for (size_t i = 0; i < KEY_LEN && key.data[i] != 0; ++i) {
|
||
|
|
hash = ((hash << 5) + hash) + key.data[i];
|
||
|
|
}
|
||
|
|
return hash;
|
||
|
|
}
|
||
|
|
};
|
||
|
|
};
|
||
|
|
|
||
|
|
typedef struct {
|
||
|
|
unsigned int maxsize;
|
||
|
|
unsigned int size;
|
||
|
|
unsigned int next;
|
||
|
|
} HEAD;
|
||
|
|
|
||
|
|
typedef struct {
|
||
|
|
FixedKey key; // 使用 FixedKey 替代 char[KEY_LEN]
|
||
|
|
TValue value;
|
||
|
|
} SM_DAT;
|
||
|
|
|
||
|
|
private:
|
||
|
|
HEAD* p_head = nullptr;
|
||
|
|
SM_DAT* p_ptr = nullptr;
|
||
|
|
|
||
|
|
// 使用共享锁提高读性能
|
||
|
|
mutable std::shared_mutex m_data_mutex;
|
||
|
|
|
||
|
|
// 使用 unordered_map 加速查找
|
||
|
|
std::unordered_map<FixedKey, unsigned int, FixedKey::Hasher> m_key_index;
|
||
|
|
mutable std::shared_mutex m_index_mutex;
|
||
|
|
|
||
|
|
// 线程本地缓存
|
||
|
|
struct ThreadCache {
|
||
|
|
FixedKey key;
|
||
|
|
unsigned int index = 0;
|
||
|
|
bool valid = false;
|
||
|
|
};
|
||
|
|
static thread_local ThreadCache s_thread_cache;
|
||
|
|
|
||
|
|
// 字符串转换优化
|
||
|
|
template<typename U = TKey>
|
||
|
|
typename std::enable_if<!std::is_same<U, std::string>::value, FixedKey>::type
|
||
|
|
toFixedKey(const TKey& tkey) const {
|
||
|
|
std::ostringstream oss;
|
||
|
|
oss << tkey;
|
||
|
|
return FixedKey(oss.str());
|
||
|
|
}
|
||
|
|
|
||
|
|
template<typename U = TKey>
|
||
|
|
typename std::enable_if<std::is_same<U, std::string>::value, FixedKey>::type
|
||
|
|
toFixedKey(const TKey& tkey) const {
|
||
|
|
return FixedKey(tkey);
|
||
|
|
}
|
||
|
|
|
||
|
|
// 初始化哈希索引
|
||
|
|
void buildIndex() {
|
||
|
|
std::unique_lock<std::shared_mutex> lock(m_index_mutex);
|
||
|
|
m_key_index.clear();
|
||
|
|
if (!p_ptr) return;
|
||
|
|
|
||
|
|
for (unsigned int i = 0; i < p_head->size; ++i) {
|
||
|
|
m_key_index[p_ptr[i].key] = i;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// 查找优化版本
|
||
|
|
int findIndexOptimized(const FixedKey& key) {
|
||
|
|
// 1. 检查线程本地缓存
|
||
|
|
if (s_thread_cache.valid && s_thread_cache.key == key) {
|
||
|
|
return static_cast<int>(s_thread_cache.index);
|
||
|
|
}
|
||
|
|
|
||
|
|
// 2. 在哈希表中查找
|
||
|
|
{
|
||
|
|
std::shared_lock<std::shared_mutex> lock(m_index_mutex);
|
||
|
|
auto it = m_key_index.find(key);
|
||
|
|
if (it != m_key_index.end()) {
|
||
|
|
s_thread_cache.key = key;
|
||
|
|
s_thread_cache.index = it->second;
|
||
|
|
s_thread_cache.valid = true;
|
||
|
|
return static_cast<int>(it->second);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
return -1;
|
||
|
|
}
|
||
|
|
|
||
|
|
// 回退到线性查找
|
||
|
|
int findIndexFallback(const FixedKey& key) {
|
||
|
|
for (unsigned int i = 0; i < p_head->size; ++i) {
|
||
|
|
if (p_ptr[i].key == key) {
|
||
|
|
// 更新索引
|
||
|
|
{
|
||
|
|
std::unique_lock<std::shared_mutex> lock(m_index_mutex);
|
||
|
|
m_key_index[key] = i;
|
||
|
|
}
|
||
|
|
s_thread_cache.key = key;
|
||
|
|
s_thread_cache.index = i;
|
||
|
|
s_thread_cache.valid = true;
|
||
|
|
return static_cast<int>(i);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return -1;
|
||
|
|
}
|
||
|
|
|
||
|
|
void init(const std::string& tableName) {
|
||
|
|
char* ptr = static_cast<char*>(GlobMem::GetInstancePtr()->GetTablePtr(tableName));
|
||
|
|
if (ptr == nullptr) {
|
||
|
|
p_ptr = nullptr;
|
||
|
|
p_head = nullptr;
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
p_head = reinterpret_cast<HEAD*>(ptr);
|
||
|
|
p_ptr = reinterpret_cast<SM_DAT*>(ptr + sizeof(HEAD));
|
||
|
|
|
||
|
|
// 构建索引
|
||
|
|
buildIndex();
|
||
|
|
}
|
||
|
|
|
||
|
|
public:
|
||
|
|
CMemMap(const std::string& tableName) {
|
||
|
|
this->init(tableName);
|
||
|
|
}
|
||
|
|
|
||
|
|
CMemMap(const std::string& tableName, unsigned int maxsize) {
|
||
|
|
this->init(tableName);
|
||
|
|
|
||
|
|
if (p_head) {
|
||
|
|
p_head->maxsize = maxsize;
|
||
|
|
if (p_head->size > p_head->maxsize) {
|
||
|
|
p_head->size = p_head->maxsize;
|
||
|
|
}
|
||
|
|
|
||
|
|
{
|
||
|
|
std::unique_lock<std::shared_mutex> lock(m_data_mutex);
|
||
|
|
if (p_head->next >= p_head->maxsize) {
|
||
|
|
p_head->next = p_head->maxsize;
|
||
|
|
}
|
||
|
|
p_head->next = p_head->next % p_head->maxsize;
|
||
|
|
}
|
||
|
|
|
||
|
|
// 重建索引
|
||
|
|
buildIndex();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
~CMemMap() = default;
|
||
|
|
|
||
|
|
// 禁用复制
|
||
|
|
CMemMap(const CMemMap&) = delete;
|
||
|
|
CMemMap& operator=(const CMemMap&) = delete;
|
||
|
|
|
||
|
|
// 移动语义
|
||
|
|
CMemMap(CMemMap&& other) noexcept
|
||
|
|
: p_head(other.p_head)
|
||
|
|
, p_ptr(other.p_ptr)
|
||
|
|
, m_key_index(std::move(other.m_key_index)) {
|
||
|
|
other.p_head = nullptr;
|
||
|
|
other.p_ptr = nullptr;
|
||
|
|
}
|
||
|
|
|
||
|
|
CMemMap& operator=(CMemMap&& other) noexcept {
|
||
|
|
if (this != &other) {
|
||
|
|
p_head = other.p_head;
|
||
|
|
p_ptr = other.p_ptr;
|
||
|
|
m_key_index = std::move(other.m_key_index);
|
||
|
|
other.p_head = nullptr;
|
||
|
|
other.p_ptr = nullptr;
|
||
|
|
}
|
||
|
|
return *this;
|
||
|
|
}
|
||
|
|
|
||
|
|
// 查找键是否存在
|
||
|
|
bool find(const TKey& tkey) {
|
||
|
|
FixedKey key = toFixedKey(tkey);
|
||
|
|
if (!p_ptr) return false;
|
||
|
|
|
||
|
|
std::shared_lock<std::shared_mutex> lock(m_data_mutex);
|
||
|
|
int idx = findIndexOptimized(key);
|
||
|
|
if (idx >= 0) return true;
|
||
|
|
|
||
|
|
// 哈希表中未找到,尝试线性查找
|
||
|
|
idx = findIndexFallback(key);
|
||
|
|
return idx >= 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
// 通过键获取值
|
||
|
|
TValue* operator[](const TKey& tkey) {
|
||
|
|
FixedKey key = toFixedKey(tkey);
|
||
|
|
if (!p_ptr) return nullptr;
|
||
|
|
|
||
|
|
std::shared_lock<std::shared_mutex> lock(m_data_mutex);
|
||
|
|
int idx = findIndexOptimized(key);
|
||
|
|
|
||
|
|
if (idx < 0) {
|
||
|
|
idx = findIndexFallback(key);
|
||
|
|
}
|
||
|
|
|
||
|
|
if (idx >= 0) {
|
||
|
|
return &p_ptr[idx].value;
|
||
|
|
}
|
||
|
|
return nullptr;
|
||
|
|
}
|
||
|
|
|
||
|
|
// 插入键值对
|
||
|
|
bool insert(const TKey& tkey, const TValue& tvalue) {
|
||
|
|
FixedKey key = toFixedKey(tkey);
|
||
|
|
if (!p_ptr) return false;
|
||
|
|
|
||
|
|
std::unique_lock<std::shared_mutex> lock(m_data_mutex);
|
||
|
|
|
||
|
|
// 查找键是否存在
|
||
|
|
int idx = findIndexOptimized(key);
|
||
|
|
if (idx < 0) {
|
||
|
|
idx = findIndexFallback(key);
|
||
|
|
}
|
||
|
|
|
||
|
|
if (idx >= 0) {
|
||
|
|
// 更新现有值
|
||
|
|
p_ptr[idx].value = tvalue;
|
||
|
|
|
||
|
|
// 更新线程缓存
|
||
|
|
s_thread_cache.key = key;
|
||
|
|
s_thread_cache.index = static_cast<unsigned int>(idx);
|
||
|
|
s_thread_cache.valid = true;
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
|
||
|
|
// 键不存在,需要插入
|
||
|
|
if (p_head->size >= p_head->maxsize) {
|
||
|
|
// 缓存已满,需要替换
|
||
|
|
unsigned int replace_index = p_head->next;
|
||
|
|
|
||
|
|
// 从哈希表中移除旧键
|
||
|
|
{
|
||
|
|
std::unique_lock<std::shared_mutex> index_lock(m_index_mutex);
|
||
|
|
m_key_index.erase(p_ptr[replace_index].key);
|
||
|
|
}
|
||
|
|
|
||
|
|
// 清除线程缓存
|
||
|
|
if (s_thread_cache.valid && s_thread_cache.index == replace_index) {
|
||
|
|
s_thread_cache.valid = false;
|
||
|
|
}
|
||
|
|
} else {
|
||
|
|
p_head->size++;
|
||
|
|
}
|
||
|
|
|
||
|
|
// 执行插入
|
||
|
|
unsigned int insert_index = p_head->next;
|
||
|
|
|
||
|
|
// 复制键值
|
||
|
|
p_ptr[insert_index].key = key;
|
||
|
|
p_ptr[insert_index].value = tvalue;
|
||
|
|
|
||
|
|
// 更新元数据
|
||
|
|
p_head->next = (p_head->next + 1) % p_head->maxsize;
|
||
|
|
|
||
|
|
// 更新哈希表
|
||
|
|
{
|
||
|
|
std::unique_lock<std::shared_mutex> index_lock(m_index_mutex);
|
||
|
|
m_key_index[key] = insert_index;
|
||
|
|
}
|
||
|
|
|
||
|
|
// 更新线程缓存
|
||
|
|
s_thread_cache.key = key;
|
||
|
|
s_thread_cache.index = insert_index;
|
||
|
|
s_thread_cache.valid = true;
|
||
|
|
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
|
||
|
|
// 指针版本
|
||
|
|
bool insert(const TKey& tkey, const TValue* tvalue) {
|
||
|
|
if (!tvalue) return false;
|
||
|
|
return insert(tkey, *tvalue);
|
||
|
|
}
|
||
|
|
|
||
|
|
// 其他接口保持不变,但使用数组下标语法提高性能
|
||
|
|
TValue* getCur(void) {
|
||
|
|
std::shared_lock<std::shared_mutex> lock(m_data_mutex);
|
||
|
|
if (!p_ptr) return nullptr;
|
||
|
|
long idx = index_cur();
|
||
|
|
if (idx < 0) return nullptr;
|
||
|
|
return &p_ptr[idx].value;
|
||
|
|
}
|
||
|
|
|
||
|
|
long index_cur(void) const {
|
||
|
|
if (!p_head || p_head->maxsize == 0) return -1;
|
||
|
|
if (p_head->next == 0) {
|
||
|
|
return static_cast<long>(p_head->maxsize - 1);
|
||
|
|
} else {
|
||
|
|
return static_cast<long>((p_head->next - 1) % p_head->maxsize);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
TValue* operator()(unsigned int i) {
|
||
|
|
std::shared_lock<std::shared_mutex> lock(m_data_mutex);
|
||
|
|
if (!p_ptr || i >= p_head->size) return nullptr;
|
||
|
|
return &p_ptr[i].value;
|
||
|
|
}
|
||
|
|
|
||
|
|
std::string getKey(unsigned int i) {
|
||
|
|
std::shared_lock<std::shared_mutex> lock(m_data_mutex);
|
||
|
|
if (!p_ptr || i >= p_head->size) return "";
|
||
|
|
return std::string(p_ptr[i].key.data);
|
||
|
|
}
|
||
|
|
|
||
|
|
bool empty(void) const {
|
||
|
|
if (!p_head) return true;
|
||
|
|
std::shared_lock<std::shared_mutex> lock(m_data_mutex);
|
||
|
|
return p_head->size == 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
unsigned int size(void) const {
|
||
|
|
if (!p_head) return 0;
|
||
|
|
std::shared_lock<std::shared_mutex> lock(m_data_mutex);
|
||
|
|
return p_head->size;
|
||
|
|
}
|
||
|
|
|
||
|
|
void clear(void) {
|
||
|
|
if (!p_head) return;
|
||
|
|
|
||
|
|
std::unique_lock<std::shared_mutex> data_lock(m_data_mutex);
|
||
|
|
std::unique_lock<std::shared_mutex> index_lock(m_index_mutex);
|
||
|
|
|
||
|
|
p_head->size = 0;
|
||
|
|
p_head->next = 0;
|
||
|
|
m_key_index.clear();
|
||
|
|
s_thread_cache.valid = false;
|
||
|
|
}
|
||
|
|
|
||
|
|
// 批量操作优化
|
||
|
|
template<typename Func>
|
||
|
|
void forEach(Func func) {
|
||
|
|
std::shared_lock<std::shared_mutex> lock(m_data_mutex);
|
||
|
|
for (unsigned int i = 0; i < p_head->size; ++i) {
|
||
|
|
func(p_ptr[i].key.toStringView(), p_ptr[i].value);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
// 初始化线程本地缓存
|
||
|
|
template<typename TKey, typename TValue>
|
||
|
|
thread_local typename CMemMap<TKey, TValue>::ThreadCache
|
||
|
|
CMemMap<TKey, TValue>::s_thread_cache;
|
||
|
|
|
||
|
|
#endif
|