C++企业项目实战(二)

简介: 教程来源 https://hllft.cn/category/software-apps.html 本节介绍KivaDB企业级基础设施与高性能数据结构:采用CMake跨平台构建,集成日志系统、不可拷贝基类;实现Redis风格SDS动态字符串(O(1)长度获取、智能内存分配);设计支持渐进式rehash的哈希表,保障高并发下KV操作低延迟与服务稳定性。

第二部分:构建系统与基础设施

2.1 CMake构建配置
企业级C++项目需要可靠的构建系统。CMake是C/C++项目的事实标准,支持跨平台构建。

# CMakeLists.txt - 顶层配置
cmake_minimum_required(VERSION 3.15)
project(KivaDB VERSION 1.0.0 LANGUAGES CXX)

# ========== C++标准配置 ==========
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)

# ========== 编译选项 ==========
# 根据构建类型设置不同的编译选项
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
    # Debug模式:关闭优化,开启调试信息,开启地址消毒器
    add_compile_options(-g -O0 -Wall -Wextra -Werror -Wno-unused-parameter)
    add_compile_options(-fsanitize=address -fsanitize=undefined)
    add_link_options(-fsanitize=address -fsanitize=undefined)
    set(CMAKE_CXX_FLAGS_DEBUG "-DDEBUG")
else()
    # Release模式:开启优化,开启LTO,生成调试符号便于线上排查
    add_compile_options(-O3 -DNDEBUG -flto -march=native)
    add_compile_options(-g -fno-omit-frame-pointer)  # 保留帧指针,便于perf采样
    add_link_options(-flto)
endif()

# ========== 平台检测 ==========
if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
    add_compile_definitions(__LINUX__)
    set(PLATFORM_LIBS pthread dl rt)
    # 检查epoll支持
    include(CheckCXXSymbolExists)
    check_cxx_symbol_exists(epoll_create sys/epoll.h HAVE_EPOLL)
    if(NOT HAVE_EPOLL)
        message(FATAL_ERROR "epoll is required on Linux")
    endif()
elseif(CMAKE_SYSTEM_NAME STREQUAL "Darwin")
    add_compile_definitions(__MACOS__)
    set(PLATFORM_LIBS pthread)
    # macOS使用kqueue
    add_compile_definitions(HAVE_KQUEUE)
elseif(CMAKE_SYSTEM_NAME STREQUAL "Windows")
    add_compile_definitions(__WINDOWS__)
    set(PLATFORM_LIBS ws2_32)
    # Windows使用IOCP
    add_compile_definitions(HAVE_IOCP)
endif()

# ========== 第三方依赖 ==========
# 设置第三方库路径
set(THIRD_PARTY_DIR ${CMAKE_SOURCE_DIR}/third_party)

# jemalloc - 高性能内存分配器
option(USE_JEMALLOC "Use jemalloc as memory allocator" ON)
if(USE_JEMALLOC)
    find_package(jemalloc)
    if(jemalloc_FOUND)
        add_compile_definitions(USE_JEMALLOC)
        set(EXTRA_LIBS ${EXTRA_LIBS} jemalloc::jemalloc)
        message(STATUS "Using jemalloc for memory allocation")
    else()
        message(STATUS "jemalloc not found, using system malloc")
    endif()
endif()

# Google Test - 单元测试
option(BUILD_TESTS "Build unit tests" ON)
if(BUILD_TESTS)
    include(FetchContent)
    FetchContent_Declare(
        googletest
        GIT_REPOSITORY https://github.com/google/googletest.git
        GIT_TAG release-1.12.1
    )
    FetchContent_MakeAvailable(googletest)
    enable_testing()
endif()

# Google Benchmark - 性能测试
option(BUILD_BENCHMARKS "Build benchmarks" OFF)
if(BUILD_BENCHMARKS)
    FetchContent_Declare(
        benchmark
        GIT_REPOSITORY https://github.com/google/benchmark.git
        GIT_TAG v1.7.1
    )
    FetchContent_MakeAvailable(benchmark)
endif()

# ========== 源代码组织 ==========
# 核心模块
set(CORE_SOURCES
    src/main.cpp
    src/core/server.cpp
    src/core/config.cpp
    src/network/event_loop.cpp
    src/network/channel.cpp
    src/network/acceptor.cpp
    src/network/connection.cpp
    src/network/tcp_server.cpp
    src/protocol/resp.cpp
    src/protocol/command.cpp
    src/datastore/dict.cpp
    src/datastore/sds.cpp
    src/datastore/object.cpp
    src/datastore/expire.cpp
    src/persistence/rdb.cpp
    src/persistence/aof.cpp
    src/replication/replication.cpp
    src/replication/heartbeat.cpp
    src/cluster/consistent_hash.cpp
    src/cluster/cluster.cpp
    src/thread/thread_pool.cpp
    src/memory/allocator.cpp
    src/memory/slab.cpp
    src/utils/logger.cpp
    src/utils/timer.cpp
    src/utils/hash.cpp
    src/utils/crc64.cpp
)

# 公共头文件目录
include_directories(
    src/include
    src
    ${CMAKE_SOURCE_DIR}
)

# 生成可执行文件
add_executable(kivadb ${CORE_SOURCES})

# 链接库
target_link_libraries(kivadb
    ${PLATFORM_LIBS}
    ${EXTRA_LIBS}
)

# ========== 编译优化选项 ==========
# 开启LTO(链接时优化)
set_target_properties(kivadb PROPERTIES INTERPROCEDURAL_OPTIMIZATION TRUE)

# 为特定目标开启高级优化
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" OR CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
    target_compile_options(kivadb PRIVATE -fno-rtti -fno-exceptions)
endif()

# ========== 安装配置 ==========
install(TARGETS kivadb DESTINATION bin)
install(FILES config/kivadb.conf DESTINATION etc/kivadb)
install(DIRECTORY scripts/ DESTINATION bin)

# ========== 测试配置 ==========
if(BUILD_TESTS)
    add_subdirectory(tests)
endif()

# ========== 打包配置 ==========
set(CPACK_GENERATOR "TGZ;ZIP")
set(CPACK_PACKAGE_NAME "kivadb")
set(CPACK_PACKAGE_VERSION ${PROJECT_VERSION})
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "High Performance Distributed KV Store")
include(CPack)

# 打印配置摘要
message(STATUS "================== KivaDB Configuration ==================")
message(STATUS "Version: ${PROJECT_VERSION}")
message(STATUS "Build Type: ${CMAKE_BUILD_TYPE}")
message(STATUS "C++ Standard: C++${CMAKE_CXX_STANDARD}")
message(STATUS "Platform: ${CMAKE_SYSTEM_NAME}")
message(STATUS "Use jemalloc: ${USE_JEMALLOC}")
message(STATUS "Build Tests: ${BUILD_TESTS}")
message(STATUS "Build Benchmarks: ${BUILD_BENCHMARKS}")
message(STATUS "===========================================================")

2.2 日志系统
日志是企业级系统的眼睛,对于问题排查、性能分析、安全审计至关重要。

// src/utils/logger.h
#ifndef KIVADB_LOGGER_H
#define KIVADB_LOGGER_H

#include <string>
#include <memory>
#include <cstdarg>
#include <mutex>

namespace kivadb {

// 日志级别
enum class LogLevel {
    DEBUG = 0,
    INFO = 1,
    WARN = 2,
    ERROR = 3,
    FATAL = 4
};

// 日志系统 - 单例模式
class Logger {
public:
    // 获取单例实例
    static Logger& getInstance() {
        static Logger instance;
        return instance;
    }

    // 设置日志级别
    void setLevel(LogLevel level) { level_ = level; }

    // 设置日志文件路径
    void setLogFile(const std::string& filename);

    // 设置是否输出到控制台
    void setConsoleOutput(bool enable) { console_output_ = enable; }

    // 日志输出宏
    void log(LogLevel level, const char* file, int line, const char* format, ...);

private:
    Logger() = default;
    ~Logger();

    Logger(const Logger&) = delete;
    Logger& operator=(const Logger&) = delete;

    std::string getLevelString(LogLevel level);
    std::string getCurrentTime();
    void writeToFile(const std::string& message);

    LogLevel level_ = LogLevel::INFO;
    std::string log_filename_;
    FILE* log_file_ = nullptr;
    bool console_output_ = true;
    std::mutex mutex_;
};

// 日志宏定义
#define LOG_DEBUG(format, ...) \
    kivadb::Logger::getInstance().log(kivadb::LogLevel::DEBUG, __FILE__, __LINE__, format, ##__VA_ARGS__)

#define LOG_INFO(format, ...) \
    kivadb::Logger::getInstance().log(kivadb::LogLevel::INFO, __FILE__, __LINE__, format, ##__VA_ARGS__)

#define LOG_WARN(format, ...) \
    kivadb::Logger::getInstance().log(kivadb::LogLevel::WARN, __FILE__, __LINE__, format, ##__VA_ARGS__)

#define LOG_ERROR(format, ...) \
    kivadb::Logger::getInstance().log(kivadb::LogLevel::ERROR, __FILE__, __LINE__, format, ##__VA_ARGS__)

#define LOG_FATAL(format, ...) \
    kivadb::Logger::getInstance().log(kivadb::LogLevel::FATAL, __FILE__, __LINE__, format, ##__VA_ARGS__)

} // namespace kivadb

#endif // KIVADB_LOGGER_H
// src/utils/logger.cpp
#include "logger.h"
#include <cstdio>
#include <ctime>
#include <cstdarg>
#include <sys/time.h>

namespace kivadb {

Logger::~Logger() {
    if (log_file_) {
        fclose(log_file_);
    }
}

void Logger::setLogFile(const std::string& filename) {
    std::lock_guard<std::mutex> lock(mutex_);
    log_filename_ = filename;
    if (log_file_) {
        fclose(log_file_);
    }
    log_file_ = fopen(filename.c_str(), "a");
    if (!log_file_) {
        fprintf(stderr, "Failed to open log file: %s\n", filename.c_str());
    }
}

std::string Logger::getLevelString(LogLevel level) {
    switch (level) {
        case LogLevel::DEBUG: return "DEBUG";
        case LogLevel::INFO:  return "INFO ";
        case LogLevel::WARN:  return "WARN ";
        case LogLevel::ERROR: return "ERROR";
        case LogLevel::FATAL: return "FATAL";
        default: return "UNKNOWN";
    }
}

std::string Logger::getCurrentTime() {
    struct timeval tv;
    gettimeofday(&tv, nullptr);

    struct tm tm_info;
    localtime_r(&tv.tv_sec, &tm_info);

    char buffer[64];
    strftime(buffer, sizeof(buffer), "%Y-%m-%d %H:%M:%S", &tm_info);

    char result[64];
    snprintf(result, sizeof(result), "%s.%03ld", buffer, tv.tv_usec / 1000);

    return std::string(result);
}

void Logger::writeToFile(const std::string& message) {
    if (log_file_) {
        fprintf(log_file_, "%s\n", message.c_str());
        fflush(log_file_);
    }
}

void Logger::log(LogLevel level, const char* file, int line, const char* format, ...) {
    if (level < level_) return;

    // 格式化消息
    char buffer[4096];
    va_list args;
    va_start(args, format);
    vsnprintf(buffer, sizeof(buffer), format, args);
    va_end(args);

    // 构建日志行
    std::string time_str = getCurrentTime();
    const char* level_str = getLevelString(level);

    // 提取文件名(去掉路径)
    const char* filename = strrchr(file, '/');
    if (filename) filename++; else filename = file;

    char log_line[8192];
    snprintf(log_line, sizeof(log_line), "[%s] [%s] [%s:%d] %s",
             time_str.c_str(), level_str, filename, line, buffer);

    // 输出到控制台
    if (console_output_) {
        fprintf(stderr, "%s\n", log_line);
        fflush(stderr);
    }

    // 输出到文件
    std::lock_guard<std::mutex> lock(mutex_);
    writeToFile(log_line);

    // FATAL级别直接终止程序
    if (level == LogLevel::FATAL) {
        abort();
    }
}

} // namespace kivadb

2.3 不可拷贝基类

// src/utils/noncopyable.h
#ifndef KIVADB_NONCOPYABLE_H
#define KIVADB_NONCOPYABLE_H

namespace kivadb {

/**
 * 不可拷贝基类
 * 
 * 继承此类可以禁止拷贝构造和赋值操作。
 * 适用于管理资源的类(如文件句柄、网络连接、互斥锁等)。
 * 
 * 示例:
 *   class MyClass : public NonCopyable {
 *       // 此类不能进行拷贝
 *   };
 */
class NonCopyable {
protected:
    NonCopyable() = default;
    ~NonCopyable() = default;

    NonCopyable(const NonCopyable&) = delete;
    NonCopyable& operator=(const NonCopyable&) = delete;

    // 移动构造和移动赋值可以保留
    NonCopyable(NonCopyable&&) = default;
    NonCopyable& operator=(NonCopyable&&) = default;
};

} // namespace kivadb

#endif // KIVADB_NONCOPYABLE_H

第三部分:高性能数据结构

3.1 SDS(Simple Dynamic String)实现
C语言字符串的痛点:获取长度需要O(n)遍历、缓冲区溢出风险、二进制不安全。SDS是Redis采用的高性能字符串实现,我们将其作为KivaDB的基础组件。

// src/datastore/sds.h
#ifndef KIVADB_SDS_H
#define KIVADB_SDS_H

#include <stddef.h>
#include <stdint.h>

namespace kivadb {

/* SDS头部类型(根据字符串长度选择,节省内存)*/
#define SDS_TYPE_5  0  // 长度 < 32
#define SDS_TYPE_8  1  // 长度 < 256
#define SDS_TYPE_16 2  // 长度 < 64K
#define SDS_TYPE_32 3  // 长度 < 4GB
#define SDS_TYPE_64 4  // 长度 < 2^63

/* 内部使用的SDS头(内存布局紧凑,使用__attribute__((packed))避免内存对齐)*/
struct __attribute__((__packed__)) sdshdr8 {
    uint8_t len;      // 已使用长度
    uint8_t alloc;    // 分配的总长度(不含header和'\0')
    unsigned char flags; // 类型标识(低3位)
    char buf[];
};

struct __attribute__((__packed__)) sdshdr16 {
    uint16_t len;
    uint16_t alloc;
    unsigned char flags;
    char buf[];
};

struct __attribute__((__packed__)) sdshdr32 {
    uint32_t len;
    uint32_t alloc;
    unsigned char flags;
    char buf[];
};

struct __attribute__((__packed__)) sdshdr64 {
    uint64_t len;
    uint64_t alloc;
    unsigned char flags;
    char buf[];
};

/* SDS类型定义 - 指向buf的指针 */
typedef char* sds;

/* ========== API声明 ========== */
// 创建与销毁
sds sdsnew(const char* init);
sds sdsnewlen(const void* init, size_t initlen);
sds sdsdup(const sds s);
void sdsfree(sds s);

// 信息获取
size_t sdslen(const sds s);
size_t sdsavail(const sds s);
size_t sdsAllocSize(const sds s);

// 修改操作
sds sdscatlen(sds s, const void* t, size_t len);
sds sdscat(sds s, const char* t);
sds sdscpylen(sds s, const char* t, size_t len);
sds sdscpy(sds s, const char* t);
sds sdsgrowzero(sds s, size_t len);
sds sdscatprintf(sds s, const char* fmt, ...);
void sdstrim(sds s, const char* cset);
void sdsrange(sds s, int start, int end);

// 比较
int sdscmp(const sds s1, const sds s2);

// 分割
sds* sdssplitlen(const char* s, int len, const char* sep, int seplen, int* count);
void sdsfreesplitres(sds* tokens, int count);

// 大小写转换
void sdstolower(sds s);
void sdstoupper(sds s);

// 数值转换
sds sdsfromlonglong(long long value);

} // namespace kivadb

#endif // KIVADB_SDS_H
// src/datastore/sds.cpp
#include "sds.h"
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <cstdarg>
#include <algorithm>

namespace kivadb {

// 辅助函数:获取头大小
static inline size_t sdsHdrSize(char type) {
    switch (type & SDS_TYPE_MASK) {
        case SDS_TYPE_5: return sizeof(struct sdshdr5);
        case SDS_TYPE_8: return sizeof(struct sdshdr8);
        case SDS_TYPE_16: return sizeof(struct sdshdr16);
        case SDS_TYPE_32: return sizeof(struct sdshdr32);
        case SDS_TYPE_64: return sizeof(struct sdshdr64);
        default: return 0;
    }
}

// 辅助函数:根据长度确定类型
static inline char sdsReqType(size_t len) {
    if (len < 32) return SDS_TYPE_5;
    if (len < 256) return SDS_TYPE_8;
    if (len < 65536) return SDS_TYPE_16;
#if (LONG_MAX == LLONG_MAX)
    if (len < 4294967296LL) return SDS_TYPE_32;
    return SDS_TYPE_64;
#else
    return SDS_TYPE_32;
#endif
}

// 获取SDS头指针
static inline struct sdshdr8* sdsheader8(const sds s) {
    return (struct sdshdr8*)(s - sizeof(struct sdshdr8));
}

static inline struct sdshdr16* sdsheader16(const sds s) {
    return (struct sdshdr16*)(s - sizeof(struct sdshdr16));
}

static inline struct sdshdr32* sdsheader32(const sds s) {
    return (struct sdshdr32*)(s - sizeof(struct sdshdr32));
}

static inline struct sdshdr64* sdsheader64(const sds s) {
    return (struct sdshdr64*)(s - sizeof(struct sdshdr64));
}

// 获取SDS类型
static inline char sdsflags(const sds s) {
    return s[-1];
}

// 获取SDS长度 - O(1)时间复杂度!
size_t sdslen(const sds s) {
    if (!s) return 0;
    unsigned char flags = sdsflags(s);
    switch (flags & SDS_TYPE_MASK) {
        case SDS_TYPE_5:
            return (size_t)(((struct sdshdr5*)s)->buf[-1] >> 3);
        case SDS_TYPE_8:
            return sdsheader8(s)->len;
        case SDS_TYPE_16:
            return sdsheader16(s)->len;
        case SDS_TYPE_32:
            return sdsheader32(s)->len;
        case SDS_TYPE_64:
            return sdsheader64(s)->len;
        default:
            return 0;
    }
}

// 获取可用空间
size_t sdsavail(const sds s) {
    if (!s) return 0;
    unsigned char flags = sdsflags(s);
    switch (flags & SDS_TYPE_MASK) {
        case SDS_TYPE_5: return 0;
        case SDS_TYPE_8: return sdsheader8(s)->alloc - sdsheader8(s)->len;
        case SDS_TYPE_16: return sdsheader16(s)->alloc - sdsheader16(s)->len;
        case SDS_TYPE_32: return sdsheader32(s)->alloc - sdsheader32(s)->len;
        case SDS_TYPE_64: return sdsheader64(s)->alloc - sdsheader64(s)->len;
        default: return 0;
    }
}

// 创建新SDS
sds sdsnewlen(const void* init, size_t initlen) {
    char type = sdsReqType(initlen);
    if (type == SDS_TYPE_5 && initlen == 0) type = SDS_TYPE_8;

    size_t hdrlen = sdsHdrSize(type);
    char* sh = (char*)malloc(hdrlen + initlen + 1);
    if (!sh) return nullptr;

    sds s = sh + hdrlen;

    // 设置头部
    switch (type) {
        case SDS_TYPE_5: {
            struct sdshdr5* h = (struct sdshdr5*)sh;
            *((uint8_t*)h) = (uint8_t)((initlen << 3) | SDS_TYPE_5);
            break;
        }
        case SDS_TYPE_8: {
            struct sdshdr8* h = (struct sdshdr8*)sh;
            h->len = initlen;
            h->alloc = initlen;
            h->flags = type;
            break;
        }
        case SDS_TYPE_16: {
            struct sdshdr16* h = (struct sdshdr16*)sh;
            h->len = initlen;
            h->alloc = initlen;
            h->flags = type;
            break;
        }
        case SDS_TYPE_32: {
            struct sdshdr32* h = (struct sdshdr32*)sh;
            h->len = initlen;
            h->alloc = initlen;
            h->flags = type;
            break;
        }
        case SDS_TYPE_64: {
            struct sdshdr64* h = (struct sdshdr64*)sh;
            h->len = initlen;
            h->alloc = initlen;
            h->flags = type;
            break;
        }
    }

    if (initlen && init) {
        memcpy(s, init, initlen);
    }
    s[initlen] = '\0';
    return s;
}

sds sdsnew(const char* init) {
    return sdsnewlen(init, init ? strlen(init) : 0);
}

// 空间预分配(Redis经典策略)
sds sdsMakeRoomFor(sds s, size_t addlen) {
    size_t avail = sdsavail(s);
    if (avail >= addlen) return s;

    size_t len = sdslen(s);
    size_t newlen = len + addlen;
    char type = sdsReqType(newlen);
    size_t hdrlen = sdsHdrSize(type);

    char* sh;

    if (type == SDS_TYPE_5) type = SDS_TYPE_8;

    size_t old_hdrlen = sdsHdrSize(sdsflags(s));

    // 核心策略:小于1MB时翻倍扩容,大于1MB时每次增加1MB
    if (newlen < SDS_MAX_PREALLOC) {
        newlen *= 2;
    } else {
        newlen += SDS_MAX_PREALLOC;
    }

    type = sdsReqType(newlen);
    hdrlen = sdsHdrSize(type);

    if (type == sdsflags(s)) {
        sh = (char*)realloc(s - old_hdrlen, hdrlen + newlen + 1);
        if (!sh) return nullptr;
        s = sh + hdrlen;
    } else {
        sh = (char*)malloc(hdrlen + newlen + 1);
        if (!sh) return nullptr;
        memcpy(sh + hdrlen, s, len + 1);
        free(s - old_hdrlen);
        s = sh + hdrlen;
    }

    // 更新头部
    switch (type) {
        case SDS_TYPE_8: {
            struct sdshdr8* h = (struct sdshdr8*)(s - hdrlen);
            h->len = len;
            h->alloc = newlen;
            h->flags = type;
            break;
        }
        case SDS_TYPE_16: {
            struct sdshdr16* h = (struct sdshdr16*)(s - hdrlen);
            h->len = len;
            h->alloc = newlen;
            h->flags = type;
            break;
        }
        case SDS_TYPE_32: {
            struct sdshdr32* h = (struct sdshdr32*)(s - hdrlen);
            h->len = len;
            h->alloc = newlen;
            h->flags = type;
            break;
        }
        case SDS_TYPE_64: {
            struct sdshdr64* h = (struct sdshdr64*)(s - hdrlen);
            h->len = len;
            h->alloc = newlen;
            h->flags = type;
            break;
        }
    }

    return s;
}

// 追加字符串
sds sdscatlen(sds s, const void* t, size_t len) {
    size_t curlen = sdslen(s);
    s = sdsMakeRoomFor(s, len);
    if (!s) return nullptr;
    memcpy(s + curlen, t, len);
    s[curlen + len] = '\0';

    // 更新长度
    unsigned char flags = sdsflags(s);
    switch (flags & SDS_TYPE_MASK) {
        case SDS_TYPE_8:
            sdsheader8(s)->len = curlen + len;
            break;
        case SDS_TYPE_16:
            sdsheader16(s)->len = curlen + len;
            break;
        case SDS_TYPE_32:
            sdsheader32(s)->len = curlen + len;
            break;
        case SDS_TYPE_64:
            sdsheader64(s)->len = curlen + len;
            break;
    }
    return s;
}

sds sdscat(sds s, const char* t) {
    return sdscatlen(s, t, strlen(t));
}

} // namespace kivadb

3.2 高性能哈希表实现
https://hllft.cn/category/tech-trends.html
哈希表是KV存储系统的核心数据结构。KivaDB实现了渐进式哈希表,解决单次扩容导致的服务卡顿问题。

// src/datastore/dict.h
#ifndef KIVADB_DICT_H
#define KIVADB_DICT_H

#include <stdint.h>
#include <cstddef>

namespace kivadb {

/* 哈希函数类型 */
typedef uint64_t (*DictHashFunction)(const void* key, size_t len);

/* 键值对节点 */
struct DictEntry {
    void* key;
    union {
        void* val;
        uint64_t u64;
        int64_t s64;
    } v;
    struct DictEntry* next;  // 链地址法解决冲突
};

/* 字典类型(多态操作)*/
struct DictType {
    uint64_t (*hashFunction)(const void* key, size_t len);
    void* (*keyDup)(void* privdata, const void* key);
    void* (*valDup)(void* privdata, const void* obj);
    int (*keyCompare)(void* privdata, const void* key1, const void* key2);
    void (*keyDestructor)(void* privdata, void* key);
    void (*valDestructor)(void* privdata, void* obj);
};

/* 哈希表(一个字典有两个哈希表,用于渐进式rehash)*/
struct Dictht {
    DictEntry** table;      // 哈希桶数组
    unsigned long size;     // 哈希表大小
    unsigned long sizemask; // 用于计算索引的掩码(size-1)
    unsigned long used;     // 已使用节点数
};

/* 字典主结构 */
struct Dict {
    DictType* type;          // 类型特定函数
    void* privdata;          // 私有数据
    Dictht ht[2];            // 两个哈希表(ht[1]用于rehash)
    int rehashidx;           // rehash进度(-1表示未进行)
    int iterators;           // 当前运行的迭代器数量
};

/* 迭代器 */
struct DictIterator {
    Dict* d;
    int table;               // 当前遍历的哈希表索引(0或1)
    int index;               // 当前桶索引
    int safe;                // 是否安全迭代器(允许修改)
    DictEntry* entry;        // 当前节点
    DictEntry* nextEntry;    // 下一个节点
};

/* API声明 */
Dict* dictCreate(DictType* type, void* privdata);
int dictExpand(Dict* d, unsigned long size);
int dictRehash(Dict* d, int n);
int dictAdd(Dict* d, void* key, void* val);
DictEntry* dictFind(Dict* d, const void* key);
void* dictFetchValue(Dict* d, const void* key);
int dictDelete(Dict* d, const void* key);
void dictRelease(Dict* d);
DictIterator* dictGetIterator(Dict* d);
DictEntry* dictNext(DictIterator* iter);
void dictReleaseIterator(DictIterator* iter);

} // namespace kivadb

#endif // KIVADB_DICT_H
// src/datastore/dict.cpp
#include "dict.h"
#include <cstdlib>
#include <cstring>
#include <cassert>

namespace kivadb {

#define DICT_HT_INITIAL_SIZE 4
#define DICT_HT_EXPAND_FACTOR 1
#define DICT_HT_SHRINK_FACTOR 10

/* 哈希函数(MurmurHash2的简化版本)*/
static uint64_t dictGenHashFunction(const void* key, size_t len) {
    const uint64_t m = 0xc6a4a7935bd1e995ULL;
    const int r = 47;
    uint64_t h = 0x1234567890abcdefULL ^ (len * m);

    const unsigned char* data = (const unsigned char*)key;
    const unsigned char* end = data + (len & ~7);

    while (data != end) {
        uint64_t k = *(uint64_t*)data;
        k *= m;
        k ^= k >> r;
        k *= m;
        h ^= k;
        h *= m;
        data += 8;
    }

    switch (len & 7) {
        case 7: h ^= (uint64_t)data[6] << 48;
        case 6: h ^= (uint64_t)data[5] << 40;
        case 5: h ^= (uint64_t)data[4] << 32;
        case 4: h ^= (uint64_t)data[3] << 24;
        case 3: h ^= (uint64_t)data[2] << 16;
        case 2: h ^= (uint64_t)data[1] << 8;
        case 1: h ^= (uint64_t)data[0];
                h *= m;
    }

    h ^= h >> r;
    h *= m;
    h ^= h >> r;

    return h;
}

/* 默认键值复制函数 */
static void* dictDupDefault(void* privdata, const void* key) {
    return (void*)key;
}

/* 默认键比较函数 */
static int dictKeyCompareDefault(void* privdata, const void* key1, const void* key2) {
    return key1 == key2;
}

/* 创建字典 */
Dict* dictCreate(DictType* type, void* privdata) {
    Dict* d = (Dict*)malloc(sizeof(Dict));
    if (!d) return nullptr;

    d->type = type ? type : &defaultDictType;
    d->privdata = privdata;
    d->rehashidx = -1;
    d->iterators = 0;
    d->ht[0] = { nullptr, 0, 0, 0 };
    d->ht[1] = { nullptr, 0, 0, 0 };

    if (dictExpand(d, DICT_HT_INITIAL_SIZE) != 0) {
        free(d);
        return nullptr;
    }

    return d;
}

/* 扩容/缩容核心函数 */
int dictExpand(Dict* d, unsigned long size) {
    // 如果正在rehash,不能再次扩容
    if (d->rehashidx != -1) return -1;

    // 确保size是2的幂
    unsigned long realsize = DICT_HT_INITIAL_SIZE;
    while (realsize < size) realsize <<= 1;

    // 创建新哈希表
    Dictht n;
    n.size = realsize;
    n.sizemask = realsize - 1;
    n.used = 0;
    n.table = (DictEntry**)calloc(realsize, sizeof(DictEntry*));
    if (!n.table) return -1;

    // 如果当前哈希表为空,直接使用新表
    if (d->ht[0].table == nullptr) {
        d->ht[0] = n;
        return 0;
    }

    // 否则设置ht[1]为新表,启动渐进式rehash
    d->ht[1] = n;
    d->rehashidx = 0;
    return 0;
}

/* 渐进式rehash(核心:分步迁移,避免单次卡顿)*/
int dictRehash(Dict* d, int n) {
    // 空转保护
    if (d->rehashidx == -1) return 0;

    int empty_visits = n * 10;  // 最多跳过10*n个空桶
    while (n-- && d->ht[0].used != 0) {
        // 跳过空桶(最多跳过empty_visits次)
        while (d->ht[0].table[d->rehashidx] == nullptr) {
            d->rehashidx++;
            if (--empty_visits == 0) return 1;
        }

        // 迁移当前桶的所有节点
        DictEntry* de = d->ht[0].table[d->rehashidx];
        while (de) {
            DictEntry* next = de->next;

            // 计算在新表中的索引
            unsigned long h = dictHashKey(d, de->key) & d->ht[1].sizemask;
            de->next = d->ht[1].table[h];
            d->ht[1].table[h] = de;

            d->ht[0].used--;
            d->ht[1].used++;
            de = next;
        }
        d->ht[0].table[d->rehashidx] = nullptr;
        d->rehashidx++;
    }

    // 检查rehash是否完成
    if (d->ht[0].used == 0) {
        free(d->ht[0].table);
        d->ht[0] = d->ht[1];
        d->ht[1] = { nullptr, 0, 0, 0 };
        d->rehashidx = -1;
        return 0;
    }

    return 1;
}

/* 添加键值对 */
int dictAdd(Dict* d, void* key, void* val) {
    // 检查是否需要扩容
    if (d->ht[0].used >= d->ht[0].size && d->rehashidx == -1) {
        dictExpand(d, d->ht[0].used * 2);
    }

    // 如果正在rehash,推进
    if (d->rehashidx != -1) dictRehash(d, 1);

    // 计算哈希值
    unsigned long h = dictHashKey(d, key);
    int table = d->rehashidx != -1 ? 1 : 0;
    unsigned long idx = h & d->ht[table].sizemask;

    // 检查键是否已存在
    DictEntry* de = d->ht[table].table[idx];
    while (de) {
        if (dictCompareKeys(d, key, de->key)) return -1;
        de = de->next;
    }

    // 创建新节点
    de = (DictEntry*)malloc(sizeof(DictEntry));
    if (!de) return -1;

    de->key = dictDupKey(d, key);
    de->v.val = dictDupVal(d, val);
    de->next = d->ht[table].table[idx];
    d->ht[table].table[idx] = de;
    d->ht[table].used++;

    return 0;
}

/* 查找键 */
DictEntry* dictFind(Dict* d, const void* key) {
    // 如果正在rehash,执行一步rehash(每次查找都推进)
    if (d->rehashidx != -1) {
        dictRehash(d, 1);
    }

    unsigned long h = dictHashKey(d, key);
    // 先在ht[0]查找
    DictEntry* de = d->ht[0].table[h & d->ht[0].sizemask];
    while (de) {
        if (dictCompareKeys(d, key, de->key)) return de;
        de = de->next;
    }

    // 如果正在rehash,再到ht[1]查找
    if (d->rehashidx != -1) {
        de = d->ht[1].table[h & d->ht[1].sizemask];
        while (de) {
            if (dictCompareKeys(d, key, de->key)) return de;
            de = de->next;
        }
    }

    return nullptr;
}

} // namespace kivadb

来源:
https://hllft.cn/category/hardware-review.html

相关文章
|
7天前
|
人工智能 数据可视化 安全
王炸组合!阿里云 OpenClaw X 飞书 CLI,开启 Agent 基建狂潮!(附带免费使用6个月服务器)
本文详解如何用阿里云Lighthouse一键部署OpenClaw,结合飞书CLI等工具,让AI真正“动手”——自动群发、生成科研日报、整理知识库。核心理念:未来软件应为AI而生,CLI即AI的“手脚”,实现高效、安全、可控的智能自动化。
34455 17
王炸组合!阿里云 OpenClaw X 飞书 CLI,开启 Agent 基建狂潮!(附带免费使用6个月服务器)
|
18天前
|
人工智能 JSON 机器人
让龙虾成为你的“公众号分身” | 阿里云服务器玩Openclaw
本文带你零成本玩转OpenClaw:学生认证白嫖6个月阿里云服务器,手把手配置飞书机器人、接入免费/高性价比AI模型(NVIDIA/通义),并打造微信公众号“全自动分身”——实时抓热榜、AI选题拆解、一键发布草稿,5分钟完成热点→文章全流程!
45284 142
让龙虾成为你的“公众号分身” | 阿里云服务器玩Openclaw
|
8天前
|
人工智能 JSON 监控
Claude Code 源码泄露:一份价值亿元的 AI 工程公开课
我以为顶级 AI 产品的护城河是模型。读完这 51.2 万行泄露的源码,我发现自己错了。
4834 20
|
1天前
|
人工智能 自然语言处理 安全
Claude Code 全攻略:命令大全 + 实战工作流(建议收藏)
本文介绍了Claude Code终端AI助手的使用指南,主要内容包括:1)常用命令如版本查看、项目启动和更新;2)三种工作模式切换及界面说明;3)核心功能指令速查表,包含初始化、压缩对话、清除历史等操作;4)详细解析了/init、/help、/clear、/compact、/memory等关键命令的使用场景和语法。文章通过丰富的界面截图和场景示例,帮助开发者快速掌握如何通过命令行和交互界面高效使用Claude Code进行项目开发,特别强调了CLAUDE.md文件作为项目知识库的核心作用。
1670 5
Claude Code 全攻略:命令大全 + 实战工作流(建议收藏)
|
7天前
|
人工智能 API 开发者
阿里云百炼 Coding Plan 售罄、Lite 停售、Pro 抢不到?最新解决方案
阿里云百炼Coding Plan Lite已停售,Pro版每日9:30限量抢购难度大。本文解析原因,并提供两大方案:①掌握技巧抢购Pro版;②直接使用百炼平台按量付费——新用户赠100万Tokens,支持Qwen3.5-Max等满血模型,灵活低成本。
1734 5
阿里云百炼 Coding Plan 售罄、Lite 停售、Pro 抢不到?最新解决方案