Skip to content

Commit

Permalink
feat: add intrusive string set entry
Browse files Browse the repository at this point in the history
  • Loading branch information
BorysTheDev committed Mar 5, 2025
1 parent ad3161b commit 2b2c81e
Show file tree
Hide file tree
Showing 3 changed files with 212 additions and 0 deletions.
1 change: 1 addition & 0 deletions src/core/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ cxx_test(interpreter_test dfly_core LABELS DFLY)

cxx_test(string_set_test dfly_core LABELS DFLY)
cxx_test(string_map_test dfly_core LABELS DFLY)
cxx_test(intrusive_string_set_test dfly_core LABELS DFLY)
cxx_test(sorted_map_test dfly_core redis_test_lib LABELS DFLY)
cxx_test(bptree_set_test dfly_core LABELS DFLY)
cxx_test(score_map_test dfly_core LABELS DFLY)
Expand Down
156 changes: 156 additions & 0 deletions src/core/intrusive_string_set.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
// Copyright 2024, DragonflyDB authors. All rights reserved.
// See LICENSE for licensing terms.
//

#pragma once

#include <cstring>
#include <memory>
#include <string_view>
#include <vector>

namespace dfly {

class ISSEntry {
public:
ISSEntry(std::string_view key) {
ISSEntry* next = nullptr;
uint32_t key_size = key.size();

auto size = sizeof(next) + sizeof(key_size) + key_size;

data_ = (char*)malloc(size);

std::memcpy(data_, &next, sizeof(next));

auto* key_size_pos = data_ + sizeof(next);
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);
}

std::string_view Key() const {
return {GetKeyData(), GetKeySize()};
}

ISSEntry* Next() const {
ISSEntry* next = nullptr;
std::memcpy(&next, data_, sizeof(next));
return next;
}

void SetNext(ISSEntry* next) {
std::memcpy(data_, &next, sizeof(next));
}

private:
const char* GetKeyData() const {
return data_ + sizeof(ISSEntry*) + sizeof(uint32_t);
}

uint32_t GetKeySize() const {
uint32_t size = 0;
std::memcpy(&size, data_ + sizeof(ISSEntry*), sizeof(size));
return size;
}

// TODO consider use SDS strings or other approach
// TODO add optimization for big keys
// memory daya layout [ISSEntry*, key_size, key]
char* data_;
};

class ISMEntry {
public:
ISMEntry(std::string_view key, std::string_view val) {
ISMEntry* next = nullptr;
uint32_t key_size = key.size();
uint32_t val_size = val.size();

auto size = sizeof(next) + sizeof(key_size) + sizeof(val_size) + key_size + val_size;

data_ = (char*)malloc(size);

std::memcpy(data_, &next, sizeof(next));

auto* key_size_pos = data_ + sizeof(next);
std::memcpy(key_size_pos, &key_size, sizeof(key_size));

auto* val_size_pos = key_size_pos + sizeof(key_size);
std::memcpy(val_size_pos, &val_size, sizeof(val_size));

auto* key_pos = val_size_pos + sizeof(val_size);
std::memcpy(key_pos, key.data(), key_size);

auto* val_pos = key_pos + key_size;
std::memcpy(val_pos, val.data(), val_size);
}

std::string_view Key() const {
return {GetKeyData(), GetKeySize()};
}

std::string_view Val() const {
return {GetValData(), GetValSize()};
}

ISMEntry* Next() const {
ISMEntry* next = nullptr;
std::memcpy(&next, data_, sizeof(next));
return next;
}

void SetVal(std::string_view val) {
// TODO add optimization for the same size key
uint32_t val_size = val.size();
auto new_size =
sizeof(ISMEntry*) + sizeof(uint32_t) + sizeof(uint32_t) + GetKeySize() + val_size;

data_ = (char*)realloc(data_, new_size);

auto* val_size_pos = data_ + sizeof(ISMEntry*) + sizeof(uint32_t);
std::memcpy(val_size_pos, &val_size, sizeof(val_size));

auto* val_pos = val_size_pos + sizeof(val_size) + GetKeySize();
std::memcpy(val_pos, val.data(), val_size);
}

void SetNext(ISMEntry* next) {
std::memcpy(data_, &next, sizeof(next));
}

private:
const char* GetKeyData() const {
return data_ + sizeof(ISMEntry*) + sizeof(uint32_t) + sizeof(uint32_t);
}

uint32_t GetKeySize() const {
uint32_t size = 0;
std::memcpy(&size, data_ + sizeof(ISMEntry*), sizeof(size));
return size;
}

const char* GetValData() const {
return GetKeyData() + GetKeySize();
}

uint32_t GetValSize() const {
uint32_t size = 0;
std::memcpy(&size, data_ + sizeof(ISMEntry*) + sizeof(uint32_t), sizeof(size));
return size;
}

// TODO consider use SDS strings or other approach
// TODO add optimization for big keys
// memory daya layout [ISMEntry*, key_size, val_size, key, val]
char* data_;
};

template <class EntryT> class IntrusiveStringSet {
public:
private:
std::vector<EntryT*> entries_;
};

} // namespace dfly
55 changes: 55 additions & 0 deletions src/core/intrusive_string_set_test.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
// Copyright 2022, DragonflyDB authors. All rights reserved.
// See LICENSE for licensing terms.
//

#include "core/intrusive_string_set.h"

#include "base/gtest.h"

namespace dfly {

using namespace std;

class IntrusiveStringSetTest : public ::testing::Test {
protected:
static void SetUpTestSuite() {
}

static void TearDownTestSuite() {
}

void SetUp() override {
}

void TearDown() override {
}
};

TEST_F(IntrusiveStringSetTest, ISSEntryTest) {
ISSEntry test("0123456789");

EXPECT_EQ(test.Key(), "0123456789"sv);
EXPECT_EQ(test.Next(), nullptr);

test.SetNext(&test);

EXPECT_EQ(test.Key(), "0123456789"sv);
EXPECT_EQ(test.Next(), &test);
}

TEST_F(IntrusiveStringSetTest, ISMEntryTest) {
ISMEntry test("0123456789", "qwertyuiopasdfghjklzxcvbnm");

EXPECT_EQ(test.Key(), "0123456789"sv);
EXPECT_EQ(test.Val(), "qwertyuiopasdfghjklzxcvbnm"sv);
EXPECT_EQ(test.Next(), nullptr);

test.SetVal("QWERTYUIOPASDFGHJKLZXCVBNM");
test.SetNext(&test);

EXPECT_EQ(test.Key(), "0123456789"sv);
EXPECT_EQ(test.Val(), "QWERTYUIOPASDFGHJKLZXCVBNM"sv);
EXPECT_EQ(test.Next(), &test);
}

} // namespace dfly

0 comments on commit 2b2c81e

Please sign in to comment.