Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: definition and implementation of the incrbyfloat command #67

Merged
merged 19 commits into from
Dec 10, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Empty file.
1 change: 1 addition & 0 deletions src/base_cmd.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ const std::string kCmdNameStrlen = "strlen";
const std::string kCmdNameSetex = "setex";
const std::string kCmdNamePsetex = "psetex";
const std::string kCmdNameSetnx = "setnx";
const std::string kCmdNameIncrbyfloat = "incrbyfloat";
const std::string kCmdNameGetBit = "getbit";

// multi
Expand Down
45 changes: 40 additions & 5 deletions src/cmd_kv.cc
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@
*/

#include "cmd_kv.h"
#include "pstd/pstd_string.h"
#include "common.h"
#include "pstd_string.h"
#include "pstd_util.h"
#include "store.h"

Expand Down Expand Up @@ -413,10 +414,10 @@ bool IncrbyCmd::DoInitial(PClient* client) {
}

void IncrbyCmd::DoCmd(PClient* client) {
int64_t new_value_ = 0;
int64_t new_value = 0;
int64_t by_ = 0;
pstd::String2int(client->argv_[2].data(), client->argv_[2].size(), &by_);
PError err = PSTORE.Incrby(client->Key(), by_, &new_value_);
PError err = PSTORE.Incrby(client->Key(), by_, &new_value);
switch (err) {
case PError_type:
client->SetRes(CmdRes::kInvalidInt);
Expand All @@ -427,14 +428,48 @@ void IncrbyCmd::DoCmd(PClient* client) {
client->AppendInteger(by_);
break;
case PError_ok:
client->AppendInteger(new_value_);
client->AppendInteger(new_value);
break;
default:
client->SetRes(CmdRes::kErrOther, "incrby cmd error");
break;
}
}

IncrbyfloatCmd::IncrbyfloatCmd(const std::string& name, int16_t arity)
: BaseCmd(name, arity, CmdFlagsWrite, AclCategoryWrite | AclCategoryString) {}

bool IncrbyfloatCmd::DoInitial(PClient* client) {
long double by_ = 0.00f;
if (StrToLongDouble(client->argv_[2].data(), client->argv_[2].size(), &by_)) {
client->SetRes(CmdRes::kInvalidFloat);
return false;
}
client->SetKey(client->argv_[1]);
return true;
}

void IncrbyfloatCmd::DoCmd(PClient* client) {
std::string new_value;
PError err = PSTORE.Incrbyfloat(client->argv_[1], client->argv_[2], &new_value);
switch (err) {
case PError_type:
AlexStocks marked this conversation as resolved.
Show resolved Hide resolved
client->SetRes(CmdRes::kInvalidFloat);
break;
case PError_notExist: // key not exist, set a new value
AlexStocks marked this conversation as resolved.
Show resolved Hide resolved
PSTORE.ClearExpire(client->Key()); // clear key's old ttl
PSTORE.SetValue(client->Key(), PObject::CreateString(client->argv_[2]));
client->AppendString(client->argv_[2]);
break;
case PError_ok:
client->AppendString(new_value);
break;
default:
client->SetRes(CmdRes::kErrOther, "incrbyfloat cmd error");
break;
}
}

SetnxCmd::SetnxCmd(const std::string& name, int16_t arity)
: BaseCmd(name, arity, CmdFlagsWrite, AclCategoryWrite | AclCategoryString) {}

Expand Down Expand Up @@ -499,4 +534,4 @@ void GetBitCmd::DoCmd(PClient* client) {
return;
}

} // namespace pikiwidb
} // namespace pikiwidb
11 changes: 11 additions & 0 deletions src/cmd_kv.h
Original file line number Diff line number Diff line change
Expand Up @@ -171,4 +171,15 @@ class GetBitCmd : public BaseCmd {
void DoCmd(PClient *client) override;
};

class IncrbyfloatCmd : public BaseCmd {
public:
IncrbyfloatCmd(const std::string &name, int16_t arity);

protected:
bool DoInitial(PClient *client) override;

private:
void DoCmd(PClient *client) override;
};

} // namespace pikiwidb
2 changes: 2 additions & 0 deletions src/cmd_table_manager.cc
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@ void CmdTableManager::InitCmdTable() {
cmds_->insert(std::make_pair(kCmdNameBitCount, std::move(bitcountPtr)));
std::unique_ptr<BaseCmd> incrbyPtr = std::make_unique<IncrbyCmd>(kCmdNameIncrby, 3);
cmds_->insert(std::make_pair(kCmdNameIncrby, std::move(incrbyPtr)));
std::unique_ptr<BaseCmd> incrbyfloatPtr = std::make_unique<IncrbyfloatCmd>(kCmdNameIncrbyfloat, 3);
cmds_->insert(std::make_pair(kCmdNameIncrbyfloat, std::move(incrbyfloatPtr)));
std::unique_ptr<BaseCmd> strlenPtr = std::make_unique<StrlenCmd>(kCmdNameStrlen, 2);
cmds_->insert(std::make_pair(kCmdNameStrlen, std::move(strlenPtr)));
std::unique_ptr<BaseCmd> setexPtr = std::make_unique<SetexCmd>(kCmdNameSetex, 4);
Expand Down
47 changes: 47 additions & 0 deletions src/common.cc
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,15 @@
*/

#include "common.h"
#include <math.h>
#include <algorithm>
#include <cerrno>
#include <chrono>
#include <cstdlib>
#include <iomanip>
#include <iostream>
#include <limits>
#include <sstream>
#include "unbounded_buffer.h"

namespace pikiwidb {
Expand Down Expand Up @@ -44,6 +47,50 @@ struct PErrorInfo g_errorInfo[] = {

int Double2Str(char* ptr, std::size_t nBytes, double val) { return snprintf(ptr, nBytes - 1, "%.6g", val); }

int StrToLongDouble(const char* s, size_t slen, long double* ldval) {
char* pEnd;
std::string t(s, slen);
if (t.find(' ') != std::string::npos) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

感觉这里的判断应该不需要吧,你在后面已经判断了转换的pEnd是否等于字符串的长度了,这里应该冗余了

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

我觉得还是留着比较好,因为我看了strtold这个函数,它可能会在遇到空格时停止转换,并返回到目前为止已经转换的数字,比如1 23.45就会变成1.0,我并不希望发生这样的事情。
举个例子,我在执行incrbyfloat test 123.45的时候因为某些原因输入成了incrbyfloat test 12 3.45,这时候test会增加12.0, 不能达到命令的预期,所以我觉得这里也是个防呆操作,只要字符串含有空格就直接抛出-1提示转换失败会更好

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

其实这个参数检查的时候应该就会检查出来吧 只接受3个参数,结果传了4个参

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

但是我觉得这个是公共方法,不是只有incrbyfloat这一个命令用到,我觉得严谨一点会更好

return -1;
}
long double d = strtold(s, &pEnd);
if (pEnd != s + slen) {
return -1;
}

if (ldval) {
*ldval = d;
}
return 0;
}

int LongDoubleToStr(long double ldval, std::string* value) {
if (isnan(ldval)) {
return -1;
} else if (isinf(ldval)) {
if (ldval > 0) {
*value = "inf";
} else {
*value = "-inf";
}
return -1;
} else {
std::ostringstream oss;
oss << std::setprecision(15) << ldval;
*value = oss.str();

// Remove trailing zeroes after the '.'
size_t dotPos = value->find('.');
if (dotPos != std::string::npos) {
value->erase(value->find_last_not_of('0') + 1, std::string::npos);
if (value->back() == '.') {
value->pop_back();
}
}
return 0;
}
}

bool TryStr2Long(const char* ptr, size_t nBytes, long& val) {
bool negtive = false;
size_t i = 0;
Expand Down
3 changes: 3 additions & 0 deletions src/common.h
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ enum PError {
PError_moduleinit = 16,
PError_moduleuninit = 17,
PError_modulerepeat = 18,
PError_overflow = 19,
PError_max,
};

Expand Down Expand Up @@ -145,6 +146,8 @@ inline std::size_t Number2Str(char* ptr, std::size_t nBytes, T val) {
}

int Double2Str(char* ptr, std::size_t nBytes, double val);
int StrToLongDouble(const char* s, size_t slen, long double* ldval);
int LongDoubleToStr(long double ldval, std::string* value);
bool TryStr2Long(const char* ptr, std::size_t nBytes, long& val); // only for decimal
bool Strtol(const char* ptr, std::size_t nBytes, long* outVal);
bool Strtoll(const char* ptr, std::size_t nBytes, long long* outVal);
Expand Down
46 changes: 45 additions & 1 deletion src/store.cc
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,12 @@
#include <cassert>
#include <limits>
#include "client.h"
#include "common.h"
#include "config.h"
#include "event_loop.h"
#include "leveldb.h"
#include "log.h"
#include "multi.h"

namespace pikiwidb {

uint32_t PObject::lruclock = static_cast<uint32_t>(::time(nullptr));
Expand Down Expand Up @@ -589,6 +589,50 @@ PError PStore::Incrby(const PString& key, int64_t value, int64_t* ret) {
return PError_ok;
}

PError PStore::Incrbyfloat(const PString& key, std::string value, std::string* ret) {
PObject* old_value = nullptr;
long double old_number = 0.00f;
long double long_double_by = 0.00f;
auto db = &dbs_[dbno_];

if (StrToLongDouble(value.data(), value.size(), &long_double_by)) {
return PError_type;
}

// shared when reading
std::unique_lock<std::shared_mutex> lock(mutex_);
PError err = getValueByType(key, old_value, PType_string);
if (err != PError_ok) {
return err;
}

auto old_number_str = pikiwidb::GetDecodedString(old_value);
// old number to long double
if (StrToLongDouble(old_number_str->c_str(), old_number_str->size(), &old_number)) {
return PError_type;
}

std::string total_string;
long double total = old_number + long_double_by;
if (LongDoubleToStr(total, &total_string)) {
return PError_overflow;
}

*ret = total_string;
PObject new_value;
new_value = PObject::CreateString(total_string);
new_value.lru = PObject::lruclock;
auto [realObj, status] = db->insert_or_assign(key, std::move(new_value));
const PObject& obj = realObj->second;

// put this key to sync list
if (!waitSyncKeys_.empty()) {
waitSyncKeys_[dbno_].insert_or_assign(key, &obj);
}

return PError_ok;
}

void PStore::SetExpire(const PString& key, uint64_t when) const { expiredDBs_[dbno_].SetExpire(key, when); }

int64_t PStore::TTL(const PString& key, uint64_t now) { return expiredDBs_[dbno_].TTL(key, now); }
Expand Down
1 change: 1 addition & 0 deletions src/store.h
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,7 @@ class PStore {
PObject* SetValue(const PString& key, PObject&& value);
// incr
PError Incrby(const PString& key, int64_t value, int64_t* ret);
PError Incrbyfloat(const PString& key, std::string value, std::string* ret);

// for expire key
enum ExpireResult : std::int8_t {
Expand Down