text:
<repo_name>longzuo/SimpleKeyValueDb<file_sep>/executor/DoOset.hpp
#ifndef SDB_DOOSET_HPP
#define SDB_DOOSET_HPP
#include "../core/Db.hpp"
#include "../core/Exception.hpp"
#include "../util/StringUtil.hpp"
namespace SDB {
// declare
void doOadd(std::vector<std::string>&, std::ostream&, Db&);
void doOdel(std::vector<std::string>&, std::ostream&, Db&);
void doOgetall(std::vector<std::string>&, std::ostream&, Db&);
// define
void doOadd(std::vector<std::string>& commands, std::ostream& out, Db& db) {
    if (commands.size() < 4) {
        throw SdbException("missing arguments!");
    }
    SDBObject::ObjPointer fres = db.find(commands[1]);
    double score = StringUtil::toDouble(commands[2]);
    if (fres.get()) {
        fres->oadd(score, std::move(commands[3]));
        return;
    }
    auto& ptr = db[std::move(commands[1])];
    if (!ptr.get()) {
        ptr = SDBObject::CreateOSetObject();
    }
    ptr->oadd(score, std::move(commands[3]));
}
void doOdel(std::vector<std::string>& commands, std::ostream& out, Db& db) {
    if (commands.size() < 3) {
        throw SdbException("missing arguments!");
    }
    auto ptr = db.find(commands[1]);
    if (ptr.get()) {
        ptr->odel(StringUtil::toDouble(commands[2]));
    } else {
        out << "null" << '\n';
    }
}
void doOgetall(std::vector<std::string>& commands, std::ostream& out, Db& db) {
    if (commands.size() < 2) {
        throw SdbException("missing arguments!");
    }
    auto ptr = db.find(commands[1]);
    if (ptr.get()) {
        ptr->print(out);
    } else {
        out << "null" << '\n';
    }
}

}  // namespace SDB
#endif<file_sep>/core/DbList.hpp
#ifndef SDB_DBLIST_HPP
#define SDB_DBLIST_HPP
#include <vector>
#include "Db.hpp"
namespace SDB {
class DbList final {
   private:
    static std::vector<Db> dblist;

   public:
    using DbListType = std::vector<Db>;
    using DbListPointer = DbListType*;
    static void init(int);
    static size_t size() { return dblist.size(); }
    // void resize(int);
    static DbListType* getDbList();
};
DbList::DbListType DbList::dblist;
void DbList::init(int size) {
    // resize
    if (dblist.size() == 0) {
        dblist.resize(size);
    }
}

DbList::DbListPointer DbList::getDbList() { return (&dblist); }
}  // namespace SDB
#endif<file_sep>/executor/DoList.hpp
#ifndef SDB_DOLIST_HPP
#define SDB_DOLIST_HPP
#include "../core/Db.hpp"
#include "../core/Exception.hpp"
namespace SDB {
// declare
void doPush(std::vector<std::string>&, std::ostream&, Db&);
void doPop(std::vector<std::string>&, std::ostream&, Db&);
void doLGetAll(std::vector<std::string>&, std::ostream&, Db&);
void doLlen(std::vector<std::string>&, std::ostream&, Db&);
// define
void doPush(std::vector<std::string>& commands, std::ostream& out, Db& db) {
    if (commands.size() < 3) {
        throw SdbException("error:missing arguments!");
    }
    SDBObject::ObjPointer fres = db.find(commands[1]);
    if (fres.get()) {
        fres->push(std::move(commands[2]));
        return;
    }
    SDBObject::ObjPointer& newptr = db[std::move(commands[1])];
    if (!newptr.get()) {
        newptr = SDBObject::CreateListObject();
    }
    newptr->push(std::move(commands[2]));
}
void doPop(std::vector<std::string>& commands, std::ostream& out, Db& db) {
    if (commands.size() < 2) {
        throw SdbException("error:missing arguments!");
    } else {
        SDBObject::ObjPointer res = db.find(commands[1]);
        if (res.get()) {
            res->pop(out);
        } else {
            out << "null" << '\n';
        }
    }
}
void doLGetAll(std::vector<std::string>& commands, std::ostream& out, Db& db) {
    if (commands.size() < 2) {
        throw SdbException("error:missing arguments!");
    } else {
        SDBObject::ObjPointer res = db.find(commands[1]);
        if (res.get()) {
            res->print(out);
        } else {
            out << "null" << '\n';
        }
    }
}
void doLlen(std::vector<std::string>& commands, std::ostream& out, Db& db) {
    if (commands.size() < 2) {
        throw SdbException("error:missing arguments!");
    } else {
        SDBObject::ObjPointer res = db.find(commands[1]);
        if (res.get()) {
            out << std::to_string(res->llen()) << '\n';
            // out<<res->llen()<<'\n';
        } else {
            out << "0" << '\n';
        }
    }
}
}  // namespace SDB
#endif<file_sep>/core/List.hpp
#ifndef SDB_LIST_HPP
#define SDB_LIST_HPP

#include <memory>

namespace SDB {
template <typename T>
class ListNode {
   public:
    T data;
    std::shared_ptr<ListNode> pre;
    std::shared_ptr<ListNode> next;
    bool hasNext() { return (next.get() != nullptr); }
    bool hasPrevious() { return (pre.get() != nullptr); }
};

template <typename T>
class List {
   public:
    using Node = ListNode<T>;
    using NodePointer = std::shared_ptr<Node>;

   private:
    NodePointer head;
    NodePointer tail;
    size_t length = 0;

   public:
    void push(const T&);
    void push(T&&);
    NodePointer pop();
    size_t len() const { return length; }
    NodePointer& first() { return head; }
    NodePointer& last() { return tail; }
    void print(std::ostream&);
};

template <typename T>
void List<T>::push(const T& _data) {
    if (head.get() == nullptr) {
        head = std::make_shared<Node>();
        tail = head;
    } else {
        tail->next = std::make_shared<Node>();
        tail = tail->next;
    }
    tail->data = _data;
    length++;
}

template <typename T>
void List<T>::push(T&& _data) {
    if (head.get() == nullptr) {
        head = std::make_shared<Node>();
        tail = head;
    } else {
        tail->next = std::make_shared<Node>();
        tail = tail->next;
    }
    tail->data = std::move(_data);
    length++;
}

template <typename T>
typename List<T>::NodePointer List<T>::pop() {
    if (length == 0) return nullptr;
    NodePointer res = head;
    head = head->next;
    length--;
    if (length == 0) {
        tail = head;
    } else {
        res->next.reset();
        head->pre.reset();
    }
    return res;
}
template <typename T>
void List<T>::print(std::ostream& out) {
    auto temp = this->head;
    while (temp.get()) {
        out << temp->data << '\n';
        temp = temp->next;
    }
}

}  // namespace SDB
#endif<file_sep>/net/NetLib.hpp
#ifndef NET_NETLIB_HPP
#define NET_NETLIB_HPP
#include <exception>
#include "unix_header.h"
namespace Net {

static const int ADDRSTRLEN = 256;

// enum class NetIPType { NetIPV4 = 1, NetIPV6 = 2 };

enum class NetDomain {
    NetUnix = AF_UNIX,
    NetInet = AF_INET,
    NetInet6 = AF_INET6,
    NetUnspecific = AF_UNSPEC
};

enum class NetSocketType {
    NetSocketStream = SOCK_STREAM,
    NetSocketDgram = SOCK_DGRAM
};

class NetException : public std::runtime_error {
   public:
    explicit NetException(const std::string& msg) : std::runtime_error(msg) {}
};

}  // namespace Net
#endif<file_sep>/core/SDBObject.hpp
#ifndef SDB_SDBOBJECT_HPP
#define SDB_SDBOBJECT_HPP
#include <atomic>
#include "Dict.hpp"
#include "Exception.hpp"
#include "IntSet.hpp"
#include "List.hpp"
#include "OrderedSet.hpp"
#include "Set.hpp"
namespace SDB {
enum class SdbObjType {
    //字符串
    SDB_STRING,
    //列表
    SDB_LIST,
    //哈希
    SDB_HASH,
    //集合
    SDB_SET,
    //有序集合
    SDB_OSET,
    //空object
    SDB_NULL
};
enum class SdbEncType {
    SDB_ENC_INT = 6,
    SDB_ENC_HT,
    SDB_ENC_RAW,
    SDB_ENC_LINKEDLIST,
    SDB_ENC_INTSET,
    SDB_ENC_SKIPLIST
};
class SDBObjectHash;
class SDBObjectCmp;
class SDBObject {
    friend class SDBObjectHash;
    friend class SDBObjectCmp;
    friend class Db;

   private:
    SdbObjType objtype;
    SdbEncType enctype;
    unsigned int lru;
    std::atomic<int>* count;
    union {
        long istr;
        std::string* str;
        List<std::string>* list;
        Dict<std::string>* dict;
        OrderedSet<std::string>* oset;
        Set<std::string>* uset;
    } value;
    void free();

   public:
    SDBObject();
    SDBObject(const SDBObject&);
    SDBObject& operator=(const SDBObject&);
    ~SDBObject();
    typedef std::shared_ptr<SDBObject> ObjPointer;
    static std::shared_ptr<SDBObject> CreateStrObject();
    static std::shared_ptr<SDBObject> CreateSetObject();
    static std::shared_ptr<SDBObject> CreateListObject();
    static std::shared_ptr<SDBObject> CreateHashObject();
    static std::shared_ptr<SDBObject> CreateOSetObject();

    // for object
    std::string getObjType();
    std::string getEncType();
    SdbObjType getEnumObjType() { return this->objtype; }

    // for String
    void set(std::string&&);
    void set(long);
    void append(const std::string&);
    ssize_t getStrLen();
    std::string getRange(size_t, size_t);

    // for list
    void push(std::string&&);
    void pop(std::ostream& out);
    ssize_t llen();

    // for hash
    void hadd(std::string&&, std::string&&);
    void hdel(const std::string&);

    // for ordered set
    void oadd(const double& socre, std::string&&);
    void odel(const double&);

    // for unordered set
    void sadd(std::string&&);
    void sdel(const std::string&);

    // SDBObject& operator=(const SDBObject&);

    void print(std::ostream& out = std::cout) const;
};
/*
struct SDBObjectHash {
    ssize_t operator()(const SDBObject& obj) const noexcept {
        if (obj.objtype == SdbObjType::SDB_STRING) {
            // if()
            return std::hash<std::string>()(*obj.value.str);
        } else {
            //只在无序集合中使用string，这里不应当进入
            return obj.value.istr;
        }
    }
};
struct SDBObjectCmp {
    bool operator()(const SDBObject& l, const SDBObject& r) const noexcept {
        if (l.objtype == SdbObjType::SDB_STRING &&
            r.objtype == SdbObjType::SDB_STRING) {
            if (l.enctype == SdbEncType::SDB_ENC_RAW &&
                r.enctype == SdbEncType::SDB_ENC_RAW) {
                if (l.value.str && r.value.str) {
                    return (*l.value.str) == *(r.value.str);
                }
            } else if (l.enctype == SdbEncType::SDB_ENC_INT &&
                       r.enctype == SdbEncType::SDB_ENC_INT) {
                if (l.value.istr == r.value.istr) {
                    return true;
                }
            }
        }
        return false;
    }
};
*/
SDBObject::SDBObject() {
    this->objtype = SdbObjType::SDB_NULL;
    this->enctype = SdbEncType::SDB_ENC_INT;
    this->count = new std::atomic<int>(1);
}
SDBObject::SDBObject(const SDBObject& oth) {
    this->objtype = oth.objtype;
    this->enctype = oth.enctype;
    this->count = oth.count;
    ++(*oth.count);
    if (this->enctype == SdbEncType::SDB_ENC_INT) {
        this->value.istr = oth.value.istr;
        return;
    }
    switch (this->objtype) {
        case SdbObjType::SDB_STRING:
            this->value.str = oth.value.str;
            break;
        case SdbObjType::SDB_HASH:
            this->value.dict = oth.value.dict;
            break;
        case SdbObjType::SDB_LIST:
            this->value.list = oth.value.list;
            break;
        case SdbObjType::SDB_OSET:
            this->value.oset = oth.value.oset;
            break;
        case SdbObjType::SDB_SET:
            this->value.uset = oth.value.uset;
            break;
        default:
            break;
    }
}
void SDBObject::free() {
    --(*this->count);
    if (*this->count != 0) return;
    if (this->enctype != SdbEncType::SDB_ENC_INT) {
        switch (this->objtype) {
            case SdbObjType::SDB_STRING:
                if (value.str) delete value.str;
                break;
            case SdbObjType::SDB_HASH:
                if (value.dict) delete value.dict;
                break;
            case SdbObjType::SDB_LIST:
                if (value.list) delete value.list;
                break;
            case SdbObjType::SDB_OSET:
                if (value.oset) delete value.oset;
                break;
            case SdbObjType::SDB_SET:
                if (value.uset) delete value.uset;
                break;
            default:
                break;
        }
    }
    delete this->count;
}
SDBObject& SDBObject::operator=(const SDBObject& oth) {
    if (&oth == this) {
        return *this;
    }
    ++(*oth.count);
    this->free();
    this->objtype = oth.objtype;
    this->enctype = oth.enctype;
    this->count = oth.count;
    if (this->enctype == SdbEncType::SDB_ENC_INT) {
        this->value.istr = oth.value.istr;
        return *this;
    }
    switch (this->objtype) {
        case SdbObjType::SDB_STRING:
            this->value.str = oth.value.str;
            break;
        case SdbObjType::SDB_HASH:
            this->value.dict = oth.value.dict;
            break;
        case SdbObjType::SDB_LIST:
            this->value.list = oth.value.list;
            break;
        case SdbObjType::SDB_OSET:
            this->value.oset = oth.value.oset;
            break;
        case SdbObjType::SDB_SET:
            this->value.uset = oth.value.uset;
            break;
        default:
            break;
    }
    return *this;
}
SDBObject::~SDBObject() { this->free(); }
std::shared_ptr<SDBObject> SDBObject::CreateStrObject() {
    return std::make_shared<SDBObject>();
}

std::shared_ptr<SDBObject> SDBObject::CreateListObject() {
    std::shared_ptr<SDBObject> temp = std::make_shared<SDBObject>();
    temp->enctype = SdbEncType::SDB_ENC_LINKEDLIST;
    temp->objtype = SdbObjType::SDB_LIST;
    temp->value.list = new List<std::string>;
    return temp;
}
std::shared_ptr<SDBObject> SDBObject::CreateSetObject() {
    auto temp = std::make_shared<SDBObject>();
    temp->enctype = SdbEncType::SDB_ENC_HT;
    temp->objtype = SdbObjType::SDB_SET;
    temp->value.uset = new Set<std::string>;
    return temp;
}
std::shared_ptr<SDBObject> SDBObject::CreateHashObject() {
    std::shared_ptr<SDBObject> temp = std::make_shared<SDBObject>();
    temp->enctype = SdbEncType::SDB_ENC_HT;
    temp->objtype = SdbObjType::SDB_HASH;
    temp->value.dict = new Dict<std::string>;
    return temp;
}

std::shared_ptr<SDBObject> SDBObject::CreateOSetObject() {
    std::shared_ptr<SDBObject> temp = std::make_shared<SDBObject>();
    temp->enctype = SdbEncType::SDB_ENC_SKIPLIST;
    temp->objtype = SdbObjType::SDB_OSET;
    temp->value.oset = new OrderedSet<std::string>;
    return temp;
}

std::string SDBObject::getObjType() {
    std::string res;
    switch (this->objtype) {
        case SdbObjType::SDB_STRING:
            res = "string";
            break;
        case SdbObjType::SDB_HASH:
            res = "hash";
            break;
        case SdbObjType::SDB_SET:
            res = "set";
            break;
        case SdbObjType::SDB_NULL:
            res = "null";
            break;
        case SdbObjType::SDB_OSET:
            res = "orderedset";
            break;
        case SdbObjType::SDB_LIST:
            res = "list";
            break;
    }
    return res;
}

std::string SDBObject::getEncType() {
    std::string res;
    switch (this->enctype) {
        case SdbEncType::SDB_ENC_INT:
            res = "int";
            break;
        case SdbEncType::SDB_ENC_RAW:
            res = "raw";
            break;
        case SdbEncType::SDB_ENC_INTSET:
            res = "intset";
            break;
        case SdbEncType::SDB_ENC_LINKEDLIST:
            res = "linkedlist";
            break;
        case SdbEncType::SDB_ENC_SKIPLIST:
            res = "skiplist";
            break;
        case SdbEncType::SDB_ENC_HT:
            res = "hashtable";
            break;
    }
    return res;
}

void SDBObject::set(std::string&& s) {
    if (this->objtype != SdbObjType::SDB_STRING &&
        this->objtype != SdbObjType::SDB_NULL) {
        throw SdbException("command is not supported for this type!");
    }

    this->objtype = SdbObjType::SDB_STRING;

    bool isNumber = true;
    bool isNeg = false;
    int64_t res = 0;
    if (s.size() <= 18) {
        unsigned int i = 0;
        if (s[0] == '-') {
            i++;
            isNeg = true;
        }
        for (; i < s.size(); ++i) {
            if (s[i] >= '0' && s[i] <= '9') {
                res = res * 10 + s[i] - '0';
            } else {
                isNumber = false;
                break;
            }
        }
        if (isNumber && isNeg) {
            res = 0 - res;
        }
    }
    if (this->enctype == SdbEncType::SDB_ENC_RAW) {
        if (this->value.str) {
            delete this->value.str;
        }
    }
    if (isNumber) {
        this->value.istr = res;
        this->enctype = SdbEncType::SDB_ENC_INT;
    } else {
        this->value.str = new std::string(std::move(s));
        this->enctype = SdbEncType::SDB_ENC_RAW;
    }
}
void SDBObject::set(long istr) {
    if (this->objtype != SdbObjType::SDB_STRING &&
        this->objtype != SdbObjType::SDB_NULL) {
        throw SdbException("command is not supported for this type!");
    }
    this->objtype = SdbObjType::SDB_STRING;
    if (this->enctype == SdbEncType::SDB_ENC_RAW) {
        if (this->value.str) {
            delete this->value.str;
        }
    }
    this->value.istr = istr;
    this->enctype = SdbEncType::SDB_ENC_INT;
}

void SDBObject::append(const std::string& data) {
    if (this->objtype == SdbObjType::SDB_STRING ||
        this->objtype == SdbObjType::SDB_NULL) {
        if (this->objtype == SdbObjType::SDB_NULL) {
            this->objtype = SdbObjType::SDB_STRING;
            this->value.str = new std::string;
        } else if (this->enctype == SdbEncType::SDB_ENC_INT) {
            long temp = this->value.istr;
            this->value.str = new std::string;
            this->value.str->append(std::to_string(temp));
        } else if (this->value.str == nullptr) {
            this->value.str = new std::string;
        }
        this->enctype = SdbEncType::SDB_ENC_RAW;
        this->value.str->append(data);
    } else {
        // error
        throw SdbException("command is not supported for this type!");
    }
}

ssize_t SDBObject::getStrLen() {
    if (this->objtype == SdbObjType::SDB_STRING) {
        if (this->enctype == SdbEncType::SDB_ENC_RAW) {
            return this->value.str->size();
        } else {
            int64_t temp = this->value.istr;
            int64_t length = 0;
            if (temp < 0) {
                temp = 0 - temp;
                ++length;
            }
            while (temp) {
                ++length;
                temp /= 10;
            }
            return length;
        }
    } else {
        // error
        throw SdbException("command is not supported for this type!");
    }
}

std::string SDBObject::getRange(size_t start, size_t end) {
    if (this->objtype != SdbObjType::SDB_STRING) {
        throw SdbException("command is not supported for this type!");
    }
    if (this->enctype == SdbEncType::SDB_ENC_RAW) {
        if (start >= this->value.str->size()) {
            return "";
        }
        return this->value.str->substr(start, end - start + 1);
    }
    std::string temp = std::to_string(this->value.istr);
    if (start >= temp.size()) {
        return "";
    }
    return temp.substr(start, end - start + 1);
}

void SDBObject::push(std::string&& s) {
    if (this->objtype != SdbObjType::SDB_LIST &&
        this->objtype != SdbObjType::SDB_NULL) {
        throw SdbException("command is not supported for this type!");
    }
    if (this->objtype == SdbObjType::SDB_NULL) {
        this->objtype = SdbObjType::SDB_LIST;
        this->enctype = SdbEncType::SDB_ENC_LINKEDLIST;
        this->value.list = new List<std::string>;
    }
    this->value.list->push(std::move(s));
}
void SDBObject::pop(std::ostream& out) {
    if (this->objtype != SdbObjType::SDB_LIST) {
        throw SdbException("command is not supported for this type!");
    }
    if (this->value.list == nullptr) {
        out << "null" << '\n';
    }
    auto temp = this->value.list->pop();
    out << temp->data << '\n';
}
ssize_t SDBObject::llen() {
    if (this->objtype != SdbObjType::SDB_LIST) {
        throw SdbException("command is not supported for this type!");
    }
    if (this->value.list == nullptr) {
        return 0;
    }
    return this->value.list->len();
}
void SDBObject::hadd(std::string&& key, std::string&& value) {
    if (this->objtype != SdbObjType::SDB_HASH) {
        throw SdbException("command is not supported for this type!");
    }
    auto& temp = this->value.dict->operator[](std::move(key));
    temp = std::move(value);
}
void SDBObject::hdel(const std::string& key) { this->value.dict->del(key); }

void SDBObject::oadd(const double& score, std::string&& value) {
    if (this->objtype != SdbObjType::SDB_OSET) {
        throw SdbException("command is not supported for this type!");
    }
    this->value.oset->insert(score, std::move(value));
}
void SDBObject::odel(const double& score) {
    if (this->objtype != SdbObjType::SDB_OSET) {
        throw SdbException("command is not supported for this type!");
    }
    this->value.oset->del(score);
}

void SDBObject::sadd(std::string&& str) {
    if (this->objtype != SdbObjType::SDB_SET) {
        throw SdbException("command is not supported for this type!");
    }
    this->value.uset->add(std::move(str));
}

void SDBObject::sdel(const std::string& str) {
    if (this->objtype != SdbObjType::SDB_SET) {
        throw SdbException("command is not supported for this type!");
    }
    this->value.uset->del(str);
}

void SDBObject::print(std::ostream& out) const {
    if (this->objtype == SdbObjType::SDB_STRING) {
        if (this->enctype == SdbEncType::SDB_ENC_INT) {
            out << std::to_string(this->value.istr) << '\n';
        } else {
            out << *(this->value.str) << '\n';
        }
    } else if (this->objtype == SdbObjType::SDB_LIST) {
        this->value.list->print(out);
    } else if (this->objtype == SdbObjType::SDB_HASH) {
        this->value.dict->print(out);
    } else if (this->objtype == SdbObjType::SDB_OSET) {
        this->value.oset->print(out);
    } else if (this->objtype == SdbObjType::SDB_SET) {
        this->value.uset->print(out);
    } else {
        throw SdbException("unknown type!");
    }
}

}  // namespace SDB

#endif<file_sep>/core/SkipList.hpp
#ifndef SDB_SKIPLIST_HPP
#define SDB_SKIPLIST_HPP
#include <chrono>
#include <iostream>
#include <memory>
#include <random>
#include <vector>
namespace SDB {

template <typename T>
class SkipListNode {
   public:
    class Level {
       public:
        //前进指针
        std::shared_ptr<SkipListNode> forward;
        //跨度
        unsigned int span = 0;
    };
    // level s数组
    std::vector<Level> level;
    //后退指针
    std::shared_ptr<SkipListNode> backward;
    //分值
    double score;
    //数据
    T data;
    auto& next(){return level[0].forward;}
};

template <typename T>
class SkipList {
   public:
    using Node = SkipListNode<T>;
    using NodePointer = std::shared_ptr<SkipListNode<T>>;

   private:
    NodePointer head;
    NodePointer tail;
    // std::shared_ptr<SkipListNode<T>> head;
    // std::shared_ptr<SkipListNode<T>> tail;
    //表中节点的数量
    unsigned long length;
    //表中层数最大的节点的层数
    int level;
    int maxLevel = 32;

    int randomLevel();

   public:
    SkipList(const int& _max_level = 32);
    std::shared_ptr<T> search(const double&);
    NodePointer insert(const double&, const T&);
    NodePointer insert(const double&, T&&);
    NodePointer& first(){return head->level[0].forward;}
    void del(const double&);
    unsigned long getRank();
    void print(std::ostream&);
    size_t len(){return length;}
};

template <typename T>
SkipList<T>::SkipList(const int& _max_level) {
    maxLevel = _max_level;
    head = std::make_shared<SkipListNode<T>>();
    head->level.resize(maxLevel);
    level = 1;
}

template <typename T>
int SkipList<T>::randomLevel() {
    std::uniform_int_distribution<int> distribution(0, 1);
    unsigned seed = std::chrono::system_clock::now().time_since_epoch().count();
    std::default_random_engine engine(seed);
    int randomlevel = 1;
    while (distribution(engine)) {
        randomlevel++;
    }
    return randomlevel > maxLevel ? maxLevel : randomlevel;
}

template <typename T>
std::shared_ptr<T> SkipList<T>::search(const double& score) {
    NodePointer node = head;
    if (node.get() == nullptr) {
        return nullptr;
    }
    for (int i = node->level.size() - 1; i >= 0; --i) {
        // next=node->level[i];
        while (node->level[i].forward.get() &&
               (node->level[i].forward->score <= score)) {
            if (node->level[i].forward->score == score) {
                // return &(node->level[i]->forward->data);
                return std::make_shared<T>(node->level[i].forward->data);
            }
            node = node->level[i].forward;
        }
    }
    return nullptr;
}

template <typename T>
std::shared_ptr<SkipListNode<T>> SkipList<T>::insert(const double& score,
                                                     const T& value) {
    NodePointer node = head;
    std::vector<NodePointer> update;
    update.resize(maxLevel);
    std::vector<unsigned int> rank;
    rank.resize(maxLevel);
    for (int i = level - 1; i >= 0; i--) {
        //从上往下查找，i从大到小，所以rank[i]=rank[i+1]
        rank[i] = i == (maxLevel - 1) ? 0 : rank[i + 1];
        /*
        while ((node->level[i].forward.get()) &&
               ((node->level[i].forward->score < socre) ||
                (node->level[i].forward->score == score) &&
                    (node->level[i].forward->data < value))) {
            // forward的score小于新socre或者forward的data小于新value，向前
            //此时rank[i]中已经存放了上一个节点的rank
            // node->level[i].span代表的是上一个节点到下一个节点的跨度
            rank[i] += node->level[i].span;
            node = node->level[i].forward;
        }
        */
        while ((node->level[i].forward.get()) &&
               (node->level[i].forward->score <= score)) {
            // forward的score小于新socre或者forward的data小于新value，向前
            //此时rank[i]中已经存放了上一个节点的rank
            // node->level[i].span代表的是上一个节点到下一个节点的跨度
            if (node->level[i].forward->score == score) {
                node->level[i].forward->data = value;
                return node->level[i].forward;
            }
            rank[i] += node->level[i].span;
            node = node->level[i].forward;
        }
        update[i] = node;
    }
    /*
    if (node->level[i].forward.get() &&
        node->level[i].forward->score == score &&
        node->level[i].forward->data == value) {
        return node->level[i].forward;
    }
    */
    // if()
    //随机产生level
    int rlevel = randomLevel();
    if (rlevel > this->level) {
        for (int i = this->level; i < rlevel; ++i) {
            rank[i] = 0;
            update[i] = head;
            //设置此处head的span为整个跳跃表的长度
            update[i]->level[i].span = length;
        }
        this->level = rlevel;
    }
    NodePointer newnode = std::make_shared<Node>();
    newnode->level.resize(rlevel);
    newnode->score = score;
    newnode->data = value;
    for (int i = 0; i < rlevel; i++) {
        newnode->level[i].forward = update[i]->level[i].forward;
        update[i]->level[i].forward = newnode;
        //如果节点都在同一个节点中，那么他们的rank都是相同的
        // rank[0]代表的是level0的rank
        //此时update[0]代表要插入的位置
        //在update[0]后插入
        newnode->level[i].span = update[i]->level[i].span - (rank[0] - rank[i]);
        //更新前一个节点的span，span至少为1
        // update[i].forward指向新增节点，span为1
        // rank[0]减去rank[i]再加1，代表到新节点的跨度
        // rank[0]是新插入的节点前一个节点的rank
        update[i]->level[i].span = (rank[0] - rank[i]) + 1;
    }
    //如果rlevel小于list中最大的level，则所有的update[i]
    //后继都不变，不过span需要+1
    for (int i = rlevel; i < level; ++i) {
        update[i]->level[i].span++;
    }
    //更新前驱指针
    //如果是除头结点外第一个节点，那么前驱为nullptr
    //否则更新为update[0]
    if (update[0].get()) {
        newnode->backward = update[0];
    }
    //如果存在后继
    //更新后继的前驱指针
    if (newnode->level[0].forward.get()) {
        newnode->level[0].forward->backward = newnode;
    } else {
        tail = newnode;
    }
    length++;
    return newnode;
}

template <typename T>
std::shared_ptr<SkipListNode<T>> SkipList<T>::insert(const double& score,
                                                     T&& value) {
    NodePointer node = head;
    std::vector<NodePointer> update;
    update.resize(maxLevel);
    std::vector<unsigned int> rank;
    rank.resize(maxLevel);
    for (int i = level - 1; i >= 0; i--) {
        //从上往下查找，i从大到小，所以rank[i]=rank[i+1]
        rank[i] = i == (maxLevel - 1) ? 0 : rank[i + 1];
        /*
        while ((node->level[i].forward.get()) &&
               ((node->level[i].forward->score < socre) ||
                (node->level[i].forward->score == score) &&
                    (node->level[i].forward->data < value))) {
            // forward的score小于新socre或者forward的data小于新value，向前
            //此时rank[i]中已经存放了上一个节点的rank
            // node->level[i].span代表的是上一个节点到下一个节点的跨度
            rank[i] += node->level[i].span;
            node = node->level[i].forward;
        }
        */
        while ((node->level[i].forward.get()) &&
               (node->level[i].forward->score <= score)) {
            // forward的score小于新socre或者forward的data小于新value，向前
            //此时rank[i]中已经存放了上一个节点的rank
            // node->level[i].span代表的是上一个节点到下一个节点的跨度
            if (node->level[i].forward->score == score) {
                node->level[i].forward->data = value;
                return node->level[i].forward;
            }
            rank[i] += node->level[i].span;
            node = node->level[i].forward;
        }
        update[i] = node;
    }
    /*
    if (node->level[i].forward.get() &&
        node->level[i].forward->score == score &&
        node->level[i].forward->data == value) {
        return node->level[i].forward;
    }
    */
    // if()
    //随机产生level
    int rlevel = randomLevel();
    if (rlevel > this->level) {
        for (int i = this->level; i < rlevel; ++i) {
            rank[i] = 0;
            update[i] = head;
            //设置此处head的span为整个跳跃表的长度
            update[i]->level[i].span = length;
        }
        this->level = rlevel;
    }
    NodePointer newnode = std::make_shared<Node>();
    newnode->level.resize(rlevel);
    newnode->score = score;
    newnode->data = std::move(value);
    for (int i = 0; i < rlevel; i++) {
        newnode->level[i].forward = update[i]->level[i].forward;
        update[i]->level[i].forward = newnode;
        //如果节点都在同一个节点中，那么他们的rank都是相同的
        // rank[0]代表的是level0的rank
        //此时update[0]代表要插入的位置
        //在update[0]后插入
        newnode->level[i].span = update[i]->level[i].span - (rank[0] - rank[i]);
        //更新前一个节点的span，span至少为1
        // update[i].forward指向新增节点，span为1
        // rank[0]减去rank[i]再加1，代表到新节点的跨度
        // rank[0]是新插入的节点前一个节点的rank
        update[i]->level[i].span = (rank[0] - rank[i]) + 1;
    }
    //如果rlevel小于list中最大的level，则所有的update[i]
    //后继都不变，不过span需要+1
    for (int i = rlevel; i < level; ++i) {
        update[i]->level[i].span++;
    }
    //更新前驱指针
    //如果是除头结点外第一个节点，那么前驱为nullptr
    //否则更新为update[0]
    if (update[0].get()) {
        newnode->backward = update[0];
    }
    //如果存在后继
    //更新后继的前驱指针
    if (newnode->level[0].forward.get()) {
        newnode->level[0].forward->backward = newnode;
    } else {
        tail = newnode;
    }
    length++;
    return newnode;
}

template <typename T>
void SkipList<T>::del(const double& score) {
    NodePointer node = head;
    std::vector<NodePointer> update;
    update.resize(this->level);
    for (int i = this->level - 1; i >= 0; i--) {
        while ((node->level[i].forward.get()) &&
               (node->level[i].forward->score < score)) {
            node = node->level[i].forward;
        }
        update[i] = node;
    }

    if (!node->level[0].forward.get() ||
        node->level[0].forward->score > score) {
        return;
    }
    NodePointer rmnode = node->level[0].forward;
    for (unsigned int i = 0; i < rmnode->level.size(); i++) {
        update[i]->level[i].forward = rmnode->level[i].forward;

        update[i]->level[i].span += rmnode->level[i].span - 1;
    }
    //如果rlevel小于list中最大的level，则所有的update[i]
    //后继都不变，不过span需要+1
    for (int i = rmnode->level.size(); i < level; ++i) {
        update[i]->level[i].span--;
    }
    //更新前驱指针
    //如果是除头结点外第一个节点，那么前驱为nullptr
    //否则更新为update[0]
    if (update[0].get()) {
        if (rmnode->level[0].forward.get()) {
            rmnode->level[0].forward->backward = update[0];
        } else {
            tail = update[0];
        }
    }
    length--;
}
template <typename T>
void SkipList<T>::print(std::ostream& out) {
    NodePointer node = head->level[0].forward;
    while (node.get()) {
        out << node->score << " " << node->data << '\n';
        node = node->level[0].forward;
    }
}
}  // namespace SDB

#endif<file_sep>/client/Makefile
default:
	make build
build:
	g++ sdbclient.cpp -o sdbclient -W
debug:
	g++ -g sdbclient.cpp -o sdbclient
clear:
	rm test<file_sep>/executor/Executor.hpp
#ifndef SDB_EXECUTOR_HPP
#define SDB_EXECUTOR_HPP
#include <functional>
#include <sstream>
#include "../core/DbList.hpp"
#include "./DoDb.hpp"
#include "./DoHash.hpp"
#include "./DoList.hpp"
#include "./DoObject.hpp"
#include "./DoOset.hpp"
#include "./DoSet.hpp"
#include "./DoString.hpp"
namespace SDB {

using DefaultFunc =
    std::function<void(std::vector<std::string>&, std::ostream&, Db&)>;
class Executor {
   private:
    DbList::DbListPointer dblistptr;
    unsigned int currentdb = 0;
    std::unordered_map<std::string, DefaultFunc> funcTables;

    void initFuncTables();

   public:
    Executor();
    void execute(const std::string&, std::ostream&);
};
Executor::Executor() {
    DbList::init(2);
    dblistptr = DbList::getDbList();
    initFuncTables();
}
void Executor::execute(const std::string& command, std::ostream& out) {
    std::vector<std::string> words;
    std::istringstream wordsStream(command);
    std::string temp;
    while (wordsStream >> temp) {
        words.push_back(std::move(temp));
    }
    if (words.size() == 0) {
        return;
    }
    auto funcIt = funcTables.find(words[0]);
    if (funcIt != funcTables.end()) {
        funcIt->second(words, out, (*dblistptr)[currentdb]);
    } else if (words[0] == "select") {
        doSelect(words, out, *dblistptr, currentdb);
    } else {
        throw SdbException("unknown command:" + words[0]);
    }
}
void Executor::initFuncTables() {
    funcTables.reserve(40);
    funcTables["object"] = doObject;
    funcTables["lpush"] = doPush;
    funcTables["lgetall"] = doLGetAll;
    funcTables["lpop"] = doPop;
    funcTables["llen"] = doLlen;
    funcTables["hadd"] = doHadd;
    funcTables["hdel"] = doHdel;
    funcTables["hgetall"] = doHgetall;
    funcTables["oadd"] = doOadd;
    funcTables["odel"] = doOdel;
    funcTables["ogetall"] = doOgetall;
    funcTables["sadd"] = doSadd;
    funcTables["sdel"] = doSdel;
    funcTables["sgetall"] = doSgetall;
    funcTables["set"] = doSset;
    funcTables["get"] = doSget;
    funcTables["append"] = doAppend;
    funcTables["strlen"] = doStrlen;
    funcTables["getrange"] = doGetRange;
    funcTables["del"] = doDel;
    funcTables["expire"] = doExpire;
    funcTables["pexpire"] = doPrecisionExpire;
    funcTables["save"] = doSave;
    funcTables["load"] = doLoad;
}

}  // namespace SDB
#endif<file_sep>/util/TimeUtil.hpp
#ifndef SDB_TIMEUTIL_HPP
#define SDB_TIMEUTIL_HPP
#include <chrono>
using std::chrono::system_clock;
namespace SDB {
class TimeUtil final {
   public:
    TimeUtil() = delete;
    static system_clock::time_point getNowTime();
    static system_clock::time_point addMillisFromNow(long);
    static system_clock::time_point addSecondsFromNow(long);
    static void addSeconds(system_clock::time_point&, long);
    static void addMillis(system_clock::time_point&, long);
    static long getDiffMillis(system_clock::time_point,
                              system_clock::time_point);
};

system_clock::time_point TimeUtil::getNowTime() {
    return std::chrono::system_clock::now();
}

system_clock::time_point TimeUtil::addMillisFromNow(long millis) {
    auto tp = std::chrono::system_clock::now();
    auto dura = std::chrono::duration<long, std::milli>(millis);
    tp += dura;
    return tp;
}

system_clock::time_point TimeUtil::addSecondsFromNow(long seconds) {
    auto tp = std::chrono::system_clock::now();
    auto dura = std::chrono::duration<long>(seconds);
    tp += dura;
    return tp;
}

void TimeUtil::addSeconds(system_clock::time_point& tp, long seconds) {
    auto dura = std::chrono::duration<long>(seconds);
    tp += dura;
}

void TimeUtil::addMillis(system_clock::time_point& tp, long millis) {
    auto dura = std::chrono::duration<long, std::milli>(millis);
    tp += dura;
}

long TimeUtil::getDiffMillis(system_clock::time_point t0,
                             system_clock::time_point t1) {
    auto nanos = t1 - t0;
    return (nanos.count() / 1000000);
}

}  // namespace SDB
#endif<file_sep>/console/Makefile
default:
	make build
build:
	g++ sdbconsole.cpp -o sdbconsole -W
debug:
	g++ -g sdbconsole.cpp -o sdbconsole
clear:
	rm sdb<file_sep>/README.md
# SimpleKeyValueDb
## 前言
一个单机简易内存键值存储,支持简易持久化，键值对存储部分参考《redis设计与实现》。将存储部分作为服务，在本地5481端口，网络部分实现参考陈硕的mudo。代码风格为C with class，使用了部分modern C++特性,代码总量为3600行，a small project。由于网络部分的限制，只能在unix环境下使用。

## 键值对存储
### 数据类型
* 字符串
* 列表
* 集合
* 有序集合
* 哈希
### 底层数据结构
* 字符串，直接采用std::string,std::string默认内存分配策略是两倍指数增长，redis 字符串是字符串大小为1M以下采用两倍策略，1M以上时每次重新分配空间都只增加1M
* 列表，采用双向链表实现，具体实现参见 core/List.hpp
* 集合，采用std::unordered_set实现.redis 是使用字典实现，只不过dict的value部分为空，只使用key部分存储；这两种底层实现都是使用的hashtable，只不过redis的hashtable的rehash策略有所不同，std::unordered_set直接新建一个新的hashtable，将旧的hashtable中的内容全部一次性复制到新的表中。redis则是新建一个hashtable，新值的插入则插入到新表中，每一次查询过程都将旧表中的内容复制到新表，直到完成。
* 有序集合，仿照redis采用跳跃表实现，具体实现参见 core/SkipList.hpp
* 哈希，采用std::unordered_map实现，redis使用字典实现，底层都是hashtable，区别不再赘述。
## 指令
目前只实现了简易的指令
### 字符串
* set key value
* get key
* strlen key
* getrange key start end
### 列表
* lpush key value
* lpop key
* lgetall key
* llen key
### 集合
* sadd key value
* sdel key value
* sgetall key
### 有序集合
* oadd key score member
* odel key socre
* ogetall key
### 哈希
* hadd key field1 field2
* hdel key field1
* hgetall key
### 数据库层面指令
* select number
* del key
* expire key seconds
* pexpire key milliseconds
* save filename
* load filename
#### 注
* select是选择当前数据库，默认为0
* expire是设定键过期指令，秒为单位，采用惰性删除策略。pexpire是设定毫秒过期时间，由于存储的是time_t，事实上毫秒会转化成秒，所以目前没有毫秒精度，有时间再更改。
* save是将当前数据库中的内容存储到指定文件中，文件内容没有压缩，是直接可读的文本内容,不过在整数存储上对int和long之类的进行了优化，前一个字节存放数字编码长度，后面紧跟的几个字节存放数字内容。
* load是将指定文件中的内容加载到当前数据库中

指令识别采用string比较，效率较低，可以改成通过哈希进行映射。

## 网络
### 实现
参考陈硕的mudo，使用epoll，reactor模式，没有官方网络库的C++写起来略微难受，不过借此机会熟悉了一把epoll。使用epoll监听事件，但是事件执行是单线程串行的，具体参考 net 目录下，略微有点混乱。

## 编译
基本上所有的代码都写在以 .hpp 为后缀的文件中，在根目录下直接make即可生成可执行文件，client可执行文件在 client 目录下，server可执行文件在 server 目录下。client和server非常简陋，仅仅是一个输入，一个执行并返回结果。server没有日志，也不是守护进程，所有信息都直接打印到控制台。

<file_sep>/net/Epoll.hpp
#ifndef SDB_EPOLL_HPP
#define SDB_EPOLL_HPP
#include <errno.h>
#include <vector>
#include "../core/Exception.hpp"
#include "IOEvent.hpp"
#include "NetLib.hpp"
#include "unix_header.h"
namespace Net {

class EpollEvents {
   public:
    struct epoll_event* events;
    int length;
    int capcity;
    int maxEvent;
    int size() { return length; }
    int maxEvents() { return maxEvent; }
    void reserve(int);
    void resize(int);
    void addEvent();
    void clear() { length = 0; }
    EpollEvents() : length(0), capcity(0), maxEvent(0), events(nullptr) {}
    EpollEvents(const EpollEvents&);
    EpollEvents(EpollEvents&&);
    EpollEvents& operator=(const EpollEvents&);
    EpollEvents& operator=(EpollEvents&&);
    epoll_event& operator[](int);
};

void EpollEvents::reserve(int nsize) {
    if (nsize <= length) {
        return;
    }
    epoll_event* nptr = new epoll_event[nsize];
    for (int i = 0; i < length; i++) {
        nptr[i] = events[i];
    }
    if (events != nullptr) {
        delete[] events;
    }
    events = nptr;
    capcity = nsize;
}

void EpollEvents::resize(int nsize) {
    if (capcity < nsize) {
        reserve(nsize);
    }
    length = nsize;
}

void EpollEvents::addEvent() {
    if (maxEvent + 1 > capcity) {
        reserve((maxEvent + 1) * 2);
    }
    maxEvent += 1;
}

EpollEvents::EpollEvents(const EpollEvents& oth) {
    if (maxEvent < oth.maxEvent) {
        reserve(oth.maxEvent);
    }
    for (int i = 0; i < oth.length; i++) {
        events[i] = oth.events[i];
    }
    length = oth.length;
    maxEvent = oth.maxEvent;
}

EpollEvents::EpollEvents(EpollEvents&& oth) {
    length = oth.length;
    capcity = oth.capcity;
    events = oth.events;
    maxEvent = oth.maxEvent;
    oth.length = 0;
    oth.capcity = 0;
    oth.events = nullptr;
    oth.maxEvent = 0;
}

EpollEvents& EpollEvents::operator=(const EpollEvents& oth) {
    if (maxEvent < oth.maxEvent) {
        reserve(oth.maxEvent);
    }
    for (int i = 0; i < oth.length; i++) {
        events[i] = oth.events[i];
    }
    length = oth.length;
    maxEvent = oth.maxEvent;
    return *this;
}

EpollEvents& EpollEvents::operator=(EpollEvents&& oth) {
    length = oth.length;
    capcity = oth.capcity;
    events = oth.events;
    maxEvent = oth.maxEvent;
    oth.length = 0;
    oth.capcity = 0;
    oth.events = nullptr;
    oth.maxEvent = 0;
    return *this;
}

epoll_event& EpollEvents::operator[](int pos) {
    if (pos >= length) {
        throw NetException("out of index of epoll events");
    }
    return events[pos];
}

class Epoll final {
   private:
    int epfd;

   public:
    Epoll();
    ~Epoll();
    bool addEvent(IOEvent&);
    bool delEvent(IOEvent&);
    bool modifyEvent(IOEvent&);
    int waitEvents(EpollEvents&, int);
};

Epoll::Epoll() {
    this->epfd = epoll_create(1024);
    if (this->epfd == -1) {
        throw NetException("can not create epoll!");
    }
}

Epoll::~Epoll() {
    if (epfd != -1) {
        ::close(epfd);
    }
}

bool Epoll::addEvent(IOEvent& e) {
    epoll_event ee = {0, 0};
    if (e.getmask() & EventStatus::EVENT_READABLE) {
        ee.events |= EPOLLIN;
    }
    if (e.getmask() & EventStatus::EVENT_WRITEABLE) {
        ee.events |= EPOLLOUT;
    }
    // int option;
    ee.data.fd = e.getfd();
    if (epoll_ctl(this->epfd, EPOLL_CTL_ADD, e.getfd(), &ee) == -1) {
        return false;
    }
    return true;
}

bool Epoll::delEvent(IOEvent& e) {
    return (epoll_ctl(this->epfd, EPOLL_CTL_DEL, e.getfd(), nullptr) != -1);
}

bool Epoll::modifyEvent(IOEvent& e) {
    epoll_event ee = {0, 0};
    ee.events = 0;
    if (e.getmask() & EventStatus::EVENT_READABLE) {
        ee.events |= EPOLLIN;
    }
    if (e.getmask() & EventStatus::EVENT_WRITEABLE) {
        ee.events |= EPOLLOUT;
    }
    ee.data.fd = e.getfd();
    if (e.getmask() != EventStatus::EVENT_NONE) {
        return (epoll_ctl(this->epfd, EPOLL_CTL_MOD, e.getfd(), &ee) != -1);
    } else {
        return (epoll_ctl(this->epfd, EPOLL_CTL_DEL, e.getfd(), &ee) != -1);
    }
}

int Epoll::waitEvents(EpollEvents& fired, int timeout) {
    int numevents = 0;
    numevents =
        epoll_wait(this->epfd, fired.events, fired.maxEvents(), timeout);
    if (numevents < 0) {
        throw NetException("epoll error,errno is:" + std::to_string(errno));
    }
    // fired.resize()
    fired.resize(numevents);
    return numevents;
}

}  // namespace Net
#endif<file_sep>/util/FileUtil.hpp
#ifndef SDB_FILEUTIL_HPP
#define SDB_FILEUTIL_HPP
#include <fstream>
#include "../core/Exception.hpp"
namespace SDB {
// declare save
enum class IntegerEncoding { INTENC8 = 12, INTENC16, INTENC32, INTENC64 };
IntegerEncoding getIntegerEncoding(unsigned long data);
int getIntegerStorageSize(IntegerEncoding);
void saveInteger(std::ofstream&, const unsigned long);
void saveInteger(std::ofstream&, const unsigned int);
void saveInteger(std::ofstream&, const unsigned short);
void saveInteger(std::ofstream&, const unsigned char);
void saveString(std::ofstream&, const std::string&);
// declare load
void loadInteger(std::ifstream&, char&);
void loadInteger(std::ifstream&, short&);
void loadInteger(std::ifstream&, int&);
void loadInteger(std::ifstream&, long&);
void loadString(std::ifstream&, std::string&, long);
// define save
IntegerEncoding getIntegerEncoding(unsigned long data) {
    if (data <= UINT8_MAX) {
        return IntegerEncoding::INTENC8;
    } else if (data <= UINT16_MAX) {
        return IntegerEncoding::INTENC16;
    } else if (data <= UINT32_MAX) {
        return IntegerEncoding::INTENC32;
    } else {
        return IntegerEncoding::INTENC64;
    }
}
int getIntegerStorageSize(IntegerEncoding enc) {
    switch (enc) {
        case IntegerEncoding::INTENC8:
            return 1;
            break;
        case IntegerEncoding::INTENC16:
            return 2;
            break;
        case IntegerEncoding::INTENC32:
            return 4;
            break;
        case IntegerEncoding::INTENC64:
            return 8;
            break;
        default:
            throw SdbException("unknown integer encoding!");
    }
}
void saveString(std::ofstream& ofs, const std::string& key) {
    size_t len = key.size();
    // ofs.write(reinterpret_cast<const char*>(&len), sizeof(size_t));
    saveInteger(ofs, len);
    ofs << key;
}
void saveInteger(std::ofstream& ofs, const unsigned long data) {
    IntegerEncoding ienc = getIntegerEncoding(data);
    int isize = getIntegerStorageSize(ienc);
    ofs.write(reinterpret_cast<const char*>(&ienc), sizeof(char));
    // ofs.write(reinterpret_cast<const char*>(&data), sizeof(long));
    uint8_t temp8;
    uint16_t temp16;
    uint32_t temp32;
    uint64_t temp64;
    switch (isize) {
        case 1:
            temp8 = static_cast<uint8_t>(data);
            ofs.write(reinterpret_cast<const char*>(&temp8), 1);
            break;
        case 2:
            temp16 = static_cast<uint16_t>(data);
            ofs.write(reinterpret_cast<const char*>(&temp16), 2);
            break;
        case 4:
            temp32 = static_cast<uint32_t>(data);
            ofs.write(reinterpret_cast<const char*>(&temp32), 4);
            break;
        case 8:
            temp64 = static_cast<uint64_t>(data);
            ofs.write(reinterpret_cast<const char*>(&temp64), 8);
            break;
        default:
            throw SdbException(
                "save integer error,encoding size is not correct!");
            break;
    }
}
void saveInteger(std::ofstream& ofs, const unsigned int data) {
    saveInteger(ofs, static_cast<const unsigned long>(data));
}
void saveInteger(std::ofstream& ofs, const unsigned short data) {
    ofs.write(reinterpret_cast<const char*>(&data), sizeof(short));
}
void saveInteger(std::ofstream& ofs, const unsigned char data) {
    ofs.write(reinterpret_cast<const char*>(&data), sizeof(char));
}

// define load
void loadInteger(std::ifstream& ifs, char& cdata) { ifs.get(cdata); }
void loadInteger(std::ifstream& ifs, short& cdata) {
    ifs.read(reinterpret_cast<char*>(&cdata), sizeof(short));
}
void loadInteger(std::ifstream& ifs, int& idata) {
    long temp = 0;
    loadInteger(ifs, temp);
    idata = static_cast<int>(temp);
}
void loadInteger(std::ifstream& ifs, long& ldata) {
    IntegerEncoding ienc = IntegerEncoding::INTENC64;
    ifs.read(reinterpret_cast<char*>(&ienc), sizeof(char));
    uint8_t temp8;
    uint16_t temp16;
    uint32_t temp32;
    uint64_t temp64;
    switch (ienc) {
        case IntegerEncoding::INTENC8:
            temp8 = 0;
            ifs.read(reinterpret_cast<char*>(&temp8), sizeof(uint8_t));
            ldata = temp8;
            break;
        case IntegerEncoding::INTENC16:
            temp16 = 0;
            ifs.read(reinterpret_cast<char*>(&temp16), sizeof(uint16_t));
            ldata = temp16;
            break;
        case IntegerEncoding::INTENC32:
            temp32 = 0;
            ifs.read(reinterpret_cast<char*>(&temp32), sizeof(uint32_t));
            ldata = temp32;
            break;
        case IntegerEncoding::INTENC64:
            temp64 = 0;
            ifs.read(reinterpret_cast<char*>(&temp64), sizeof(uint64_t));
            ldata = temp64;
            break;
        default:
            throw SdbException("load integer error,unknown encoding type!");
            break;
    }
}
void loadString(std::ifstream& ifs, std::string& str, long len) {
    if (len <= 0) return;
    str.reserve(str.size() + len);
    char ch;
    while (len > 0 && ifs.get(ch)) {
        str.push_back(ch);
        len--;
    }
}
}  // namespace SDB
#endif<file_sep>/net/TcpServer.hpp
#ifndef NET_TCPSERVER_HPP
#define NET_TCPSERVER_HPP
#include <map>
#include <memory>
#include "Epoll.hpp"
#include "TcpConnection.hpp"
#include "TcpSocket.hpp"
namespace Net {

class TcpServer {
   private:
    InetAddress localAddress;
    TcpSocket tcpsocket;
    Epoll epoll;
    std::map<int, std::shared_ptr<TcpConnection>> connections;
    std::map<int, std::shared_ptr<IOEvent>> eventpool;
    EpollEvents activeEvents;
    std::vector<int> fdtoClose;

   public:
    TcpServer(InetAddress&);
    void newConnectionHandle();
    void addNewConnection(int, TcpConnectionPtr&);
    void rmEventConnection();
    void run();
    void notifyClose(std::shared_ptr<TcpConnection>);
    bool addEvent(IOEvent& e) { return epoll.addEvent(e); }
    bool delEvent(IOEvent& e) { return epoll.delEvent(e); }
    bool modifyEvent(IOEvent& e) { return epoll.modifyEvent(e); }

    virtual void onMessageCallback(std::shared_ptr<TcpConnection>) = 0;
    virtual void onCloseCallback(std::shared_ptr<TcpConnection>) = 0;
    virtual void onConnectedCallback(std::shared_ptr<TcpConnection>) = 0;
    virtual void onWriteCallback(std::shared_ptr<TcpConnection>) = 0;
};

TcpServer::TcpServer(InetAddress& addr) {
    if (!tcpsocket.create(NetDomain::NetInet, NetSocketType::NetSocketStream,
                          0)) {
        throw NetException("create socket failed!");
    }
    if (!tcpsocket.bind(addr)) {
        throw NetException("bind socket failed!");
    }
    if (!tcpsocket.listen()) {
        throw NetException("listen socket failed!");
    }
    auto accevent = std::make_shared<IOEvent>(tcpsocket.getfd());
    accevent->setReadFunc(std::bind(&TcpServer::newConnectionHandle, this));
    eventpool.insert({tcpsocket.getfd(), accevent});
    accevent->setReadable();
    addEvent(*accevent);
    activeEvents.reserve(10);
    activeEvents.addEvent();
}

void TcpServer::newConnectionHandle() {
    InetAddress peerAddress;
    int nfd = tcpsocket.accept(peerAddress);
    auto newconnection = std::make_shared<TcpConnection>(nfd, peerAddress);
    newconnection->setOnMessageCallback(
        std::bind(&TcpServer::onMessageCallback, this, std::placeholders::_1));
    newconnection->setOnCloseCallback(
        std::bind(&TcpServer::onCloseCallback, this, std::placeholders::_1));
    newconnection->setOnWriteCallback(
        std::bind(&TcpServer::onWriteCallback, this, std::placeholders::_1));
    newconnection->setNotifyClose(
        std::bind(&TcpServer::notifyClose, this, std::placeholders::_1));
    addNewConnection(nfd, newconnection);
    onConnectedCallback(newconnection);
}

void TcpServer::addNewConnection(int fd, TcpConnectionPtr& ptr) {
    connections.insert({fd, ptr});
    eventpool.insert({fd, ptr->getEvent()});
    ptr->getEvent()->setReadable();
    addEvent(*(ptr->getEvent()));
    activeEvents.addEvent();
}

void TcpServer::notifyClose(std::shared_ptr<TcpConnection> conn) {
    //
    fdtoClose.push_back(conn->getEvent()->getfd());
    delEvent(*conn->getEvent());
    ::close(conn->getEvent()->getfd());
}

void TcpServer::run() {
    while (true) {
        epoll.waitEvents(activeEvents, -1);
        for (int i = 0; i < activeEvents.size(); i++) {
            auto eptr = eventpool.find(activeEvents[i].data.fd);
            if (eptr != eventpool.end()) {
                eptr->second->handleEvent(activeEvents[i].events);
            }
        }
        // close handle
        activeEvents.clear();
        for (unsigned int i = 0; i < fdtoClose.size(); i++) {
            eventpool.erase(fdtoClose[i]);
            connections.erase(fdtoClose[i]);
        }
        fdtoClose.clear();
        // epoll delete
    }
}

}  // namespace Net

#endif<file_sep>/client/SdbClient.hpp
#ifndef SDB_CLIENT_HPP
#define SDB_CLIENT_HPP
#include <iostream>
#include "../core/Exception.hpp"
#include "../net/TcpSocket.hpp"
namespace SDB {

class SdbClient {
   private:
    Net::TcpSocket tcpsocket;

   public:
    SdbClient(Net::InetAddress&);
    void send(const std::string&);
    std::string&& read();
};

SdbClient::SdbClient(Net::InetAddress& addr) {
    if (!tcpsocket.connect(addr)) {
        throw SdbException("can not connect to server!");
    }
}

void SdbClient::send(const std::string& msg) {
    if (msg.size() == 0) {
        return;
    }
    size_t total = 0;
    size_t send = 0;
    size_t start = 0;
    std::string msgheader;
    msgheader.append("sdb");
    total += msgheader.size() + msg.size() + sizeof(total);
    msgheader.append(reinterpret_cast<const char*>(&total), sizeof(total));
    total = msgheader.size();
    char* pos = const_cast<char*>(msgheader.c_str());
    while (total > 0) {
        // send 表示每一次写入的字节数
        send = tcpsocket.write(pos + start, total);
        total -= send;
        // start 表示已经写入的字节数，也是下一次写入开始的地址
        start += send;
    }
    total = msg.size();
    send = 0;
    pos = const_cast<char*>(msg.c_str());
    start = 0;
    while (total > 0) {
        send = tcpsocket.write(pos + start, total);
        total -= send;
        start += send;
    }
}

std::string&& SdbClient::read() {
    char buf[512];
    std::string res;
    size_t bytes;
    size_t received = 0;
    bool getlen = false;
    size_t toreceive = 0;
    while ((bytes = tcpsocket.read(buf, 512)) > 0) {
        res.append(buf, bytes);
        received += bytes;
        if (received >= (sizeof(size_t) + 3)) {
            char* pos = reinterpret_cast<char*>(&toreceive);
            for (unsigned int i = 0; i < sizeof(size_t); ++i) {
                pos[i] = res[3 + i];
            }
            getlen = true;
        }
        if (getlen && received >= toreceive) {
            break;
        }
    }
    std::string temp(res, sizeof(size_t) + 3);
    return std::move(temp);
}

}  // namespace SDB

#endif<file_sep>/net/IOEvent.hpp
#ifndef NET_IOEVENT_HPP
#define NET_IOEVENT_HPP
#include <functional>
#include <memory>
#include "Event.hpp"
#include "unix_header.h"
namespace Net {

using IODefaultFunc = std::function<void()>;

class IOEvent {
   private:
    int fd;
    int mask;
    IODefaultFunc readFunc;
    IODefaultFunc wirteFunc;
    IODefaultFunc closeFunc;

   public:
    IOEvent(int _fd) : fd(_fd), mask(0) {}
    void setReadFunc(const IODefaultFunc&);
    void setWriteFunc(const IODefaultFunc&);
    void setCloseFunc(const IODefaultFunc&);
    void handleEvent(uint32_t);
    int getfd() { return fd; }
    int getmask() { return mask; }
    void setReadable() { mask |= EventStatus::EVENT_READABLE; }
    void setWriteable() { mask |= EventStatus::EVENT_WRITEABLE; }
    void unsetReadable() { mask &= (~EventStatus::EVENT_READABLE); }
    void unsetWriteable() { mask &= (~EventStatus::EVENT_WRITEABLE); }
    void setMask(int mk) { mask = mk; }
};

void IOEvent::setReadFunc(const IODefaultFunc& rf) {
    //
    readFunc = rf;
}

void IOEvent::setWriteFunc(const IODefaultFunc& wf) {
    //
    wirteFunc = wf;
}

void IOEvent::setCloseFunc(const IODefaultFunc& cf) {
    // close
    closeFunc = cf;
}

void IOEvent::handleEvent(uint32_t events) {
    if ((events & EPOLLHUP) && !(events & EPOLLIN)) {
        if (closeFunc) closeFunc();
    }

    if (events & EPOLLERR || events & EPOLLRDHUP) {
        if (closeFunc) closeFunc();
    }
    if (events & (EPOLLIN | EPOLLPRI | EPOLLRDHUP)) {
        if (readFunc) readFunc();
    }
    if (events & EPOLLOUT) {
        if (wirteFunc) wirteFunc();
    }
}

using IOEventPtr = std::shared_ptr<IOEvent>;

}  // namespace Net

#endif<file_sep>/console/sdbconsole.cpp
#include <algorithm>
#include <atomic>
#include <chrono>
#include <iostream>
#include <random>
#include <string>
#include "Console.hpp"
using namespace SDB;
int main() {
    Console console;
    console.run();
}<file_sep>/net/Socket.hpp
#ifndef NET_SOCKET_HPP
#define NET_SOCKET_HPP
#include "InetAddress.hpp"
namespace Net {

class Socket {
   private:
    int fd;
    int backlog;
    NetDomain dm;
    bool hasCreated;

   public:
    Socket(int);
    Socket(const Socket&) = delete;
    Socket& operator=(const Socket&) = delete;
    ~Socket();

    bool create(NetDomain, NetSocketType, int);
    bool bind(InetAddress&);
    int getfd();
    void closefd();
    NetDomain getDomain() const { return dm; }
    size_t read(void*, size_t);
    size_t write(const void*, size_t);
};

Socket::Socket(int _backlog = 1) {
    fd = -1;
    backlog = _backlog;
    dm = NetDomain::NetUnspecific;
    hasCreated = false;
}

bool Socket::create(NetDomain domain,
                    NetSocketType tp = NetSocketType::NetSocketStream,
                    int protocol = 0) {
    fd = socket(static_cast<int>(domain), static_cast<int>(tp), protocol);
    if (fd != -1) {
        hasCreated = true;
        dm = domain;
        return true;
    } else {
        return false;
    }
}

bool Socket::bind(InetAddress& address) {
    if (address.dm == NetDomain::NetInet) {
        this->dm = address.dm;
        return (::bind(fd, (struct sockaddr*)(&address.addr.addr4),
                       sizeof(address.addr.addr4)) == 0);
    } else if (address.dm == NetDomain::NetInet6) {
        this->dm = address.dm;
        return (::bind(fd, (struct sockaddr*)&address.addr.addr6,
                       sizeof(address.addr.addr6)) == 0);
    } else {
        return false;
    }
}

int Socket::getfd() { return fd; }

Socket::~Socket() { this->closefd(); }

void Socket::closefd() {
    if (hasCreated && fd != -1) {
        ::close(fd);
        fd = -1;
        hasCreated = false;
    }
}

size_t Socket::read(void* buf, size_t count) {
    // read
    return ::read(fd, buf, count);
}
size_t Socket::write(const void* buf, size_t count) {
    return ::write(fd, buf, count);
}

}  // namespace Net
#endif<file_sep>/executor/DoDb.hpp
#ifndef SDB_DODB_HPP
#define SDB_DODB_HPP
#include "../core/Db.hpp"
#include "../core/Exception.hpp"
namespace SDB {
// declare
void doSelect(const std::vector<std::string>&, std::ostream&, std::vector<Db>&,
              unsigned int&);
void doDel(const std::vector<std::string>&, std::ostream&, Db&);
void doExpire(std::vector<std::string>&, std::ostream&, Db&);
void doPrecisionExpire(std::vector<std::string>&, std::ostream&, Db&);
void doSave(const std::vector<std::string>&, std::ostream&, Db&);
void doLoad(const std::vector<std::string>&, std::ostream&, Db&);
// define
void doSelect(const std::vector<std::string>& commands, std::ostream&,
              std::vector<Db>& dblist, unsigned int& currentdb) {
    unsigned int selectdb = 0;
    for (unsigned int i = 0; i < commands[1].size(); ++i) {
        if (commands[1][i] >= '0' && commands[1][i] <= '9') {
            selectdb = selectdb * 10 + commands[1][i] - '0';
        } else {
            throw SdbException("error:input is not a number!");
            return;
        }
    }
    if (selectdb == currentdb) {
        return;
    }
    if (selectdb < 10) {
        if (selectdb > dblist.size()) {
            dblist.resize(selectdb);
        }
        currentdb = selectdb;
    } else {
        throw SdbException("error:number of db is too large!");
    }
}
void doDel(const std::vector<std::string>& commands, std::ostream& out,
           Db& db) {
    db.del(commands[1]);
}
void doExpire(std::vector<std::string>& commands, std::ostream& out, Db& db) {
    if (commands.size() < 3) {
        throw SdbException("error:missing arguments!");
    }
    db.addExpire(commands[1], commands[2]);
}

void doPrecisionExpire(std::vector<std::string>& commands, std::ostream& out,
                       Db& db) {
    if (commands.size() < 3) {
        throw SdbException("error:missing arguments!");
    }
    db.addPrecisionExpire(commands[1], commands[2]);
}

void doSave(const std::vector<std::string>& commands, std::ostream&, Db& db) {
    if (commands.size() < 2) {
        throw SdbException("error:missing arguments!");
    }
    db.save(commands[1]);
}

void doLoad(const std::vector<std::string>& commands, std::ostream&, Db& db) {
    if (commands.size() < 2) {
        throw SdbException("error:missing arguments!");
    }
    db.load(commands[1]);
}

}  // namespace SDB
#endif<file_sep>/core/IntSet.hpp
#ifndef SDB_INTSET_HPP
#define SDB_INTSET_HPP
#include <iostream>
#include <vector>
namespace SDB{
//直接存放3个桶(vector)，根据大小存放到不同的桶
class IntSet {
   private:
    std::vector<int16_t> i16set;
    std::vector<int32_t> i32set;
    std::vector<int64_t> i64set;
    //正数负数0都存放在同一个桶中
    //因此需要记录每个通的负数个数
    //查找的时候根据数据大小选择桶进行查找
    //输出的时候则需要先输出每个桶的负数再输出0和正数
    unsigned int i16NegCount = 0;
    unsigned int i32NegCount = 0;
    unsigned int i64NegCount = 0;

   public:
    void del(const int64_t&);
    bool contains(const int64_t&);
    uint32_t size() const {
        return i16set.size() + i32set.size() + i64set.size();
    }
    void print();
    void insert(const int64_t&);
    // INT32_MAX
};
bool IntSet::contains(const int64_t& data) {
    unsigned int i;
    if (data <= INT16_MAX && data >= INT16_MIN) {
        // find in i16set;
        //采用顺序查找，可以改进为二分查找
        for (i = 0; i < i16set.size(); ++i) {
            if (i16set[i] == data) {
                return true;
            } else if (i16set[i] > data) {
                return false;
            }
        }
    } else if (data <= INT32_MAX && data >= INT32_MIN) {
        // find in i32set;
        for (i = 0; i < i32set.size(); ++i) {
            if (i32set[i] == data) {
                return true;
            } else if (i32set[i] > data) {
                return false;
            }
        }
    } else {
        // find in i64set;
        for (i = 0; i < i64set.size(); ++i) {
            if (i64set[i] == data) {
                return true;
            } else if (i64set[i] > data) {
                return false;
            }
        }
    }
    return false;
}
void IntSet::insert(const int64_t& data) {
    if (this->contains(data)) return;
    int i = 0;
    if (data <= INT16_MAX && data >= INT16_MIN) {
        // insert into i16set
        int16_t temp = data;
        i16set.push_back(temp);
        int i = i16set.size() - 2;
        while (i >= 0 && temp < i16set[i]) {
            i16set[i + 1] = i16set[i];
            --i;
        }
        i16set[i + 1] = temp;
        if (data < 0) {
            ++i16NegCount;
        }
    } else if (data <= INT32_MAX && data >= INT32_MIN) {
        // insert into i32set
        int32_t temp = data;
        i32set.push_back(temp);
        int i = i32set.size() - 2;
        while (i >= 0 && temp < i32set[i]) {
            i32set[i + 1] = i32set[i];
            --i;
        }
        i32set[i + 1] = temp;
        if (data < 0) {
            ++i32NegCount;
        }
    } else {
        // insert into i64set;
        int64_t temp = data;
        i64set.push_back(temp);
        int i = i64set.size() - 2;
        while (i >= 0 && temp < i64set[i]) {
            i64set[i + 1] = i64set[i];
            --i;
        }
        i64set[i + 1] = temp;
        if (data < 0) {
            ++i64NegCount;
        }
    }
}
void IntSet::print() {
    unsigned int i = 0;
    for (i = 0; i < i64NegCount; ++i) {
        std::cout << i64set[i] << " ";
    }
    for (i = 0; i < i32NegCount; ++i) {
        std::cout << i32set[i] << " ";
    }
    for (i = 0; i < i16NegCount; ++i) {
        std::cout << i16set[i] << " ";
    }
    for (i = i16NegCount; i < i16set.size(); ++i) {
        std::cout << i16set[i] << " ";
    }
    for (i = i32NegCount; i < i32set.size(); ++i) {
        std::cout << i32set[i] << " ";
    }
    for (i = i64NegCount; i < i64set.size(); ++i) {
        std::cout << i64set[i] << " ";
    }
    std::cout << std::endl;
}
void IntSet::del(const int64_t& data) {
    unsigned int i = 0;
    if (data <= INT16_MAX && data >= INT16_MIN) {
        // find in i16set;
        //采用顺序查找，可以改进为二分查找
        for (; i < i16set.size(); ++i) {
            if (i16set[i] == data) {
                break;
            } else if (i16set[i] > data) {
                return;
            }
        }
        if (i < i16set.size()) {
            i16set.erase(i16set.begin() + i);
            if (data < 0) {
                --i16NegCount;
            }
        }
    } else if (data <= INT32_MAX && data >= INT32_MIN) {
        // find in i32set;
        for (; i < i32set.size(); ++i) {
            if (i32set[i] == data) {
                break;
            } else if (i32set[i] > data) {
                return;
            }
        }
        if (i < i32set.size()) {
            i32set.erase(i32set.begin() + i);
            if (data < 0) {
                --i32NegCount;
            }
        }
    } else {
        // find in i64set;
        for (; i < i64set.size(); ++i) {
            if (i64set[i] == data) {
                break;
            } else if (i64set[i] > data) {
                return;
            }
        }
        if (i < i64set.size()) {
            i64set.erase(i64set.begin() + i);
            if (data < 0) {
                --i64NegCount;
            }
        }
    }
}

}  // namespace

#endif<file_sep>/util/StringUtil.hpp
#ifndef SDB_STRINGUTIL_HPP
#define SDB_STRINGUTIL_HPP
#include "../core/Exception.hpp"
namespace SDB {
class StringUtil final {
   public:
    static double toDouble(const std::string&);
    static std::string&& toString(double);
};

double StringUtil::toDouble(const std::string& str) {
    if (str.empty()) {
        throw SdbException("generate a double from an empty string!");
    }
    double res = 0;
    int integer = 0;
    bool isNeg = false;
    unsigned int i = 0;
    if (str[0] == '-') {
        if (str.size() == 1) {
            throw SdbException("can not convert to double from:" + str);
        }
        isNeg = true;
        ++i;
    }
    for (; i < str.size() && str[i] != '.'; ++i) {
        if (str[i] >= '0' && str[i] <= '9') {
            integer = integer * 10 + str[i] - '0';
        } else {
            throw SdbException("can not convert to double from:" + str);
        }
    }
    res = integer;
    integer = 0;
    unsigned int start = 0;
    if (i < str.size() && str[i] == '.') {
        ++i;
        start = i;
        for (; i < str.size(); ++i) {
            if (str[i] >= '0' && str[i] <= '9') {
                integer = integer * 10 + str[i] - '0';
            } else {
                throw SdbException("can not convert to double from:" + str);
            }
        }
    }
    if (integer != 0) {
        unsigned int count = str.size() - start;
        double temp = integer;
        while (count) {
            temp /= 10;
            count--;
        }
        res += temp;
    }
    if (isNeg) {
        res = 0 - res;
    }
    return res;
}
std::string&& StringUtil::toString(double d) {
    std::string res = std::to_string(d);
    int i = res.size() - 1;
    //删除无效的0
    while (i >= 0 && res[i] == '0') --i;
    //小数点以后全为0，删除小数点
    if (i > 0 && res[i] == '.') {
        --i;
    }
    res.resize(i + 1);
    return std::move(res);
}

}  // namespace SDB
#endif<file_sep>/executor/DoObject.hpp
#ifndef SDB_DOOBJECT_HPP
#define SDB_DOOBJECT_HPP
#include "../core/Db.hpp"
#include "../core/Exception.hpp"
namespace SDB {
// declare
void doObject(const std::vector<std::string>&, std::ostream&, Db&);
void doEncoding(const std::vector<std::string>&, std::ostream&, Db&);
void doType(const std::vector<std::string>&, std::ostream&, Db&);

// define
void doObject(const std::vector<std::string>& commands, std::ostream& out,
              Db& db) {
    if (commands.size() < 3) {
        throw SdbException("missing arguments!");
    }
    if (commands[1] == "encoding") {
        doEncoding(commands, out, db);
    } else if (commands[1] == "type") {
        doType(commands, out, db);
    } else {
        throw SdbException("unknown sub command:" + commands[1]);
    }
}

void doEncoding(const std::vector<std::string>& commands, std::ostream& out,
                Db& db) {
    SDBObject::ObjPointer res = db.find(commands[2]);
    if (res.get()) {
        out << res->getEncType() << '\n';
    } else {
        out << "null" << '\n';
    }
}

void doType(const std::vector<std::string>& commands, std::ostream& out,
            Db& db) {
    SDBObject::ObjPointer res = db.find(commands[2]);
    if (res.get()) {
        out << res->getObjType() << '\n';
    } else {
        out << "null" << '\n';
    }
}
}  // namespace SDB
#endif<file_sep>/server/Makefile
default:
	make build
build:
	g++ sdbserver.cpp -o sdbserver -W
debug:
	g++ -g sdbserver.cpp -o sdbserver
clear:
	rm test<file_sep>/core/OrderedSet.hpp
#ifndef SDB_ORDEREDSET_HPP
#define SDB_ORDEREDSET_HPP
#include "./SkipList.hpp"
namespace SDB {
template <typename T>
class OrderedSet {
    friend class Db;
   private:
    SkipList<T> slist;

   public:
    OrderedSet(const int& _max_level = 32) : slist(_max_level) {}
    typename SkipList<T>::NodePointer insert(const double& score,
                                             const T& value);
    typename SkipList<T>::NodePointer insert(const double& score, T&& value);
    void del(const double&);
    void print(std::ostream&);
};

template <typename T>
typename SkipList<T>::NodePointer OrderedSet<T>::insert(const double& score,
                                                        const T& value) {
    return slist.insert(score, value);
}
template <typename T>
typename SkipList<T>::NodePointer OrderedSet<T>::insert(const double& score,
                                                        T&& value) {
    return slist.insert(score, std::move(value));
}

template <typename T>
void OrderedSet<T>::del(const double& score) {
    slist.del(score);
}
template <typename T>
void OrderedSet<T>::print(std::ostream& out) {
    this->slist.print(out);
}

}  // namespace SDB
#endif<file_sep>/core/Dict.hpp
#ifndef SDB_DICT_HPP
#define SDB_DICT_HPP
#include <unordered_map>
namespace SDB {

// unordered_map 不是渐进式rehash，如果有必要可以自定义哈希表数据结构
template <typename Value>
class Dict {
    friend class Db;
   private:
    std::unordered_map<std::string, Value> umap;

   public:
    size_t size() { return umap.size(); }
    void clear() { umap.clear(); }

    void add(const std::string& key, const Value& value);
    void add(std::string&& key, Value&& value);
    void replace(const std::string& key, const Value& value);
    void replace(std::string&& key, Value&& value);
    Value get(const std::string& key) { return umap[key]; }
    void del(const std::string&);
    auto& operator[](const std::string& key);
    auto& operator[](std::string&& key);
    void print(std::ostream&);
};

template <typename Value>
void Dict<Value>::add(const std::string& key, const Value& value) {
    umap.insert({key, value});
}

template <typename Value>
void Dict<Value>::add(std::string&& key, Value&& value) {
    umap.insert(std::move({std::move(key), std::move(value)}));
}

template <typename Value>
void Dict<Value>::replace(const std::string& key, const Value& value) {
    umap[key] = value;
}

template <typename Value>
void Dict<Value>::replace(std::string&& key, Value&& value) {
    umap[std::move(key)] = std::move(value);
}

template <typename Value>
void Dict<Value>::del(const std::string& key) {
    umap.erase(key);
}
template <typename Value>
auto& Dict<Value>::operator[](const std::string& key) {
    return umap[key];
}

template <typename Value>
auto& Dict<Value>::operator[](std::string&& key) {
    return umap[key];
}

template <typename Value>
void Dict<Value>::print(std::ostream& out) {
    for (auto it = umap.begin(); it != umap.end(); ++it) {
        out << it->first << " " << it->second << '\n';
    }
}

}  // namespace SDB

#endif<file_sep>/server/sdbserver.cpp
#include <iostream>
#include "SdbServer.hpp"
using namespace Net;
using namespace SDB;
int main() {
    InetAddress address = InetAddress::IPV4AddressAny();
    address.setPort(5481);
    SdbServer sdbserver(address);
    sdbserver.run();
}<file_sep>/net/Buffer.hpp
#ifndef NET_BUFFER_HPP
#define NET_BUFFER_HPP
#include "string"
#include "unix_header.h"
namespace Net {

class Buffer {
   private:
    std::string buffer;
    int readstart;

   public:
    Buffer(int);
    Buffer(const Buffer&);
    Buffer(Buffer&&);
    ~Buffer() = default;
    Buffer& operator<<(char c);
    Buffer& operator<<(const char*);
    std::string&& readBufferAsString();
    std::string copyBufferAsString(size_t);
    size_t capcity();
    size_t size();
    size_t readFromFd(int);
    size_t writeToFd(int);
    size_t push(const char*, size_t);
    size_t push(const std::string&);
    size_t push(std::string&&);
    void clear();
};

Buffer::Buffer(int size = 256) {
    buffer.reserve(size);
    readstart = 0;
}

Buffer& Buffer::operator<<(char c) {
    // push back
    buffer.push_back(c);
    return *this;
}

Buffer& Buffer::operator<<(const char* s) {
    size_t i = 0;
    buffer.append(s);
    return *this;
}

void Buffer::clear() {
    buffer.clear();
    readstart = 0;
}

std::string&& Buffer::readBufferAsString() {
    // return
    if (size() == 0) {
        return std::move(std::string(""));
    }
    if (readstart == 0) {
        return std::move(buffer);
    } else {
        std::string res(buffer, readstart);
        clear();
        return std::move(res);
    }
}

std::string Buffer::copyBufferAsString(size_t len) {
    if (size() > 0) {
        if (len > size()) {
            len = size();
        }
        return std::string(buffer, readstart, len);
    } else {
        return std::string();
    }
}

size_t Buffer::capcity() {
    //
    return buffer.capacity();
}

size_t Buffer::size() {
    //
    return buffer.size() - readstart;
}

size_t Buffer::push(const char* buf, size_t siz) {
    if (siz <= 0) {
        return 0;
    }
    buffer.reserve(buffer.size() + siz * 2);
    buffer.append(buf, siz);
    return siz;
}

size_t Buffer::push(const std::string& msg) {
    // add
    buffer += msg;
}

size_t Buffer::push(std::string&& msg) {
    if (size() == 0) {
        buffer = std::move(msg);
    } else {
        buffer.append(msg);
    }
    return msg.size();
}

size_t Buffer::readFromFd(int fd) {
    if (fd == -1) {
        return 0;
    }
    // int flags, preflags;
    // flags = preflags = fcntl(fd, F_GETFL, 0);
    // flags |= O_NONBLOCK;
    // flags |= O_NDELAY;
    // fcntl(fd, F_SETFL, flags);
    char buf[512];
    size_t bytes = 0;
    bytes = ::read(fd, buf, 512);
    if (bytes > 0) {
        buffer.append(buf, bytes);
    }

    // reset flags
    // fcntl(fd, F_SETFL, preflags);
    return bytes;
}

size_t Buffer::writeToFd(int fd) {
    // size_t total = size();
    if (size() == 0) {
        return 0;
    }
    size_t wcount = 0;
    // int flags, preflags;
    // flags = preflags = fcntl(fd, F_GETFL, 0);
    // flags |= O_NONBLOCK;
    // flags |= O_NDELAY;
    // fcntl(fd, F_SETFL, flags);
    char* start = const_cast<char*>(buffer.c_str());
    if ((wcount = ::write(fd, start + readstart, size())) > 0) {
        readstart += wcount;
    }
    //如果写完了就清空buffer
    //使下一次push从开始处添加
    if (size() == 0) {
        clear();
    }
    // fcntl(fd, F_SETFL, preflags);
    return wcount;
}

}  // namespace Net

#endif<file_sep>/core/Db.hpp
#ifndef SDB_DB_HPP
#define SDB_DB_HPP
#include <chrono>
#include <ctime>
#include <fstream>
#include <map>
#include <shared_mutex>
#include <unordered_set>
#include "../util/FileUtil.hpp"
#include "../util/StringUtil.hpp"
#include "SDBObject.hpp"
namespace SDB {

const unsigned char EXPIRE = 16;
class Db {
   private:
    //采用智能指针存储，方便在查询结果中返回nullptr，同时减少copy时的拷贝量
    //另外，SDBObject没有引用计数的时候，使用智能指针管理，加上了引用计数后为了方便
    //没有改。。。。
    std::map<std::string, SDBObject::ObjPointer> db;
    //存储时间戳
    std::unordered_map<std::string, std::time_t> expire;

    int checkOutOfDate(const std::string&);
    void saveObjectType(std::ofstream&, const SDBObject::ObjPointer&);
    void saveStringObject(std::ofstream&, const SDBObject::ObjPointer&);
    void saveHashObject(std::ofstream&, const SDBObject::ObjPointer&);
    void saveListObject(std::ofstream&, const SDBObject::ObjPointer&);
    void saveOsetObject(std::ofstream&, const SDBObject::ObjPointer&);
    void saveSetObject(std::ofstream&, const SDBObject::ObjPointer&);

    void loadStringObject(std::ifstream&, bool, std::string&);
    void loadHashObject(std::ifstream&, bool, std::string&);
    void loadListObject(std::ifstream&, bool, std::string&);
    void loadOsetObject(std::ifstream&, bool, std::string&);
    void loadSetObject(std::ifstream&, bool, std::string&);
    // void loadFrom(const std::string&);

   public:
    void add(const std::string& key, const SDBObject::ObjPointer& objptr);
    void add(std::string&& key, const SDBObject::ObjPointer& objptr);
    void addExpire(const std::string& key, const std::string&);
    void addPrecisionExpire(const std::string& key, const std::string&);
    void delExpire(const std::string& key);
    void del(const std::string& key);
    void save(const std::string&);
    void load(const std::string&);

    SDBObject::ObjPointer find(const std::string&);
    SDBObject::ObjPointer& operator[](const std::string&);
    SDBObject::ObjPointer& operator[](std::string&&);
};

void Db::add(const std::string& key, const SDBObject::ObjPointer& objptr) {
    db.insert({key, objptr});
}

void Db::add(std::string&& key, const SDBObject::ObjPointer& objptr) {
    db[std::move(key)] = objptr;
}

void Db::addExpire(const std::string& key, const std::string& seconds) {
    using namespace std::chrono;
    long sec = StringUtil::toDouble(seconds);
    system_clock::duration ttl = duration<long>(sec);
    auto deadline = system_clock::now() + ttl;
    auto timet = system_clock::to_time_t(deadline);
    this->expire.insert({key, timet});
}

void Db::addPrecisionExpire(const std::string& key, const std::string& millis) {
    using namespace std::chrono;
    long milliseconds = StringUtil::toDouble(millis);
    system_clock::duration ttl = duration<long, std::milli>(milliseconds);
    auto deadline = system_clock::now() + ttl;
    auto timet = system_clock::to_time_t(deadline);
    this->expire.insert({key, timet});
}

void Db::del(const std::string& key) { db.erase(key); }

int Db::checkOutOfDate(const std::string& key) {
    using namespace std::chrono;
    auto eit = this->expire.find(key);
    //存在于expire中
    if (eit != this->expire.end()) {
        auto now = system_clock::now();
        //已经过期，返回1
        if (now > system_clock::from_time_t(eit->second)) {
            return 1;
        } else {
            //未过期，返回-1
            return -1;
        }
    }
    //未找到，返回0
    return 0;
}

SDBObject::ObjPointer Db::find(const std::string& key) {
    if (checkOutOfDate(key) == 1) {
        this->expire.erase(key);
        this->db.erase(key);
        return nullptr;
    }
    auto it = db.find(key);
    if (it != db.end()) {
        return it->second;
    } else {
        return nullptr;
    }
}

SDBObject::ObjPointer& Db::operator[](const std::string& key) {
    return db[key];
}

SDBObject::ObjPointer& Db::operator[](std::string&& key) {
    return db[std::move(key)];
}
void Db::saveObjectType(std::ofstream& ofs, const SDBObject::ObjPointer& obj) {
    saveInteger(ofs, static_cast<unsigned char>(obj->objtype));
}
/*
.sdb文件
|SDB|db_version|key-value|EOF|

key-value
    有过期时间:|expire|time_t|type|key|value|
    无过期时间:|type|key|value|
key
    raw string
value
    string
string
    raw:|enc|size|str value|
    int:|enc|value|
*/
void Db::saveStringObject(std::ofstream& ofs,
                          const SDBObject::ObjPointer& obj) {
    saveInteger(ofs, static_cast<unsigned char>(obj->enctype));
    if (obj->enctype == SdbEncType::SDB_ENC_INT) {
        // long
        saveInteger(ofs, static_cast<unsigned long>(obj->value.istr));
    } else {
        // string
        saveString(ofs, *obj->value.str);
    }
}

void Db::saveHashObject(std::ofstream& ofs, const SDBObject::ObjPointer& obj) {
    // hash object
    auto umap = obj->value.dict->umap;
    saveInteger(ofs, umap.size());
    for (auto it = umap.begin(); it != umap.end(); ++it) {
        saveString(ofs, it->first);
        saveString(ofs, it->second);
    }
}

void Db::saveListObject(std::ofstream& ofs, const SDBObject::ObjPointer& obj) {
    // list object
    // auto head=obj->value.list->first;
    saveInteger(ofs, obj->value.list->len());
    auto head = (*(obj->value.list)).first();
    while (head.get()) {
        saveString(ofs, head->data);
        head = head->next;
    }
}

void Db::saveSetObject(std::ofstream& ofs, const SDBObject::ObjPointer& obj) {
    // set object
    auto uset = obj->value.uset->uset;
    saveInteger(ofs, uset.size());
    for (auto it = uset.begin(); it != uset.end(); ++it) {
        saveString(ofs, *it);
    }
}

void Db::saveOsetObject(std::ofstream& ofs, const SDBObject::ObjPointer& obj) {
    // ordered set object
    auto sklist = obj->value.oset->slist;
    saveInteger(ofs, sklist.len());
    auto ptr = sklist.first();
    while (ptr.get()) {
        saveString(ofs, StringUtil::toString(ptr->score));
        saveString(ofs, ptr->data);
        ptr = ptr->next();
    }
}

void Db::save(const std::string& filename) {
    std::ofstream ofs(filename + ".sdb", std::ofstream::out |
                                             std::ofstream::trunc |
                                             std::ofstream::binary);
    unsigned int version = 1;
    ofs << "SDB";
    saveInteger(ofs, version);
    auto expireCount = expire.size();
    for (auto dbit = db.begin(); dbit != db.end();) {
        //如果还有过期的时间记录，判断当前需要写入的 key-value 是否过期
        if (expireCount > 0) {
            int res = checkOutOfDate(dbit->first);
            if (res == 1) {
                expire.erase(dbit->first);
                // auto newit=db.erase(dbit->first);
                db.erase(dbit++);
                //直接continue，跳过写入
                continue;
            } else if (res == -1) {
                //有过期时间，写入
                saveInteger(ofs, EXPIRE);
                saveInteger(ofs,
                            static_cast<unsigned long>(expire[dbit->first]));
            }
        }
        // write to file
        // save type
        saveObjectType(ofs, dbit->second);
        // save key
        saveString(ofs, dbit->first);
        // std::cout<<"save key:"<<dbit->first<<std::endl;
        switch (dbit->second->getEnumObjType()) {
            case SdbObjType::SDB_STRING:
                saveStringObject(ofs, dbit->second);
                break;
            case SdbObjType::SDB_HASH:
                saveHashObject(ofs, dbit->second);
                break;
            case SdbObjType::SDB_LIST:
                saveListObject(ofs, dbit->second);
                break;
            case SdbObjType::SDB_SET:
                saveSetObject(ofs, dbit->second);
                break;
            case SdbObjType::SDB_OSET:
                saveOsetObject(ofs, dbit->second);
                break;
            default:
                break;
        }
        ++dbit;
    }
    ofs.close();
}
void Db::loadStringObject(std::ifstream& ifs, bool tobesave, std::string& key) {
    //
    char etype = 0;
    loadInteger(ifs, etype);
    SdbEncType enctype = static_cast<SdbEncType>(etype);
    if (enctype == SdbEncType::SDB_ENC_INT) {
        long istr = 0;
        loadInteger(ifs, istr);
        if (tobesave) {
            auto& ptr = this->db[std::move(key)];
            if (!ptr.get()) {
                ptr = SDBObject::CreateStrObject();
            }
            ptr->set(istr);
        }
    } else if (enctype == SdbEncType::SDB_ENC_RAW) {
        std::string str;
        long size = 0;
        loadInteger(ifs, size);
        loadString(ifs, str, size);
        if (tobesave) {
            // this->db[std::move(key)]->set(std::move(str));
            auto& ptr = this->db[std::move(key)];
            if (!ptr.get()) {
                ptr = SDBObject::CreateStrObject();
            }
            ptr->set(std::move(str));
        }
    }
}
void Db::loadListObject(std::ifstream& ifs, bool tobesave, std::string& key) {
    //
    long length = 0;
    loadInteger(ifs, length);
    auto& ptr = this->db[key];
    if (tobesave) {
        if (!ptr.get()) {
            ptr = SDBObject::CreateListObject();
        }
    }
    while (length > 0 && ifs.peek() != EOF) {
        long strsize = 0;
        std::string str;
        loadInteger(ifs, strsize);
        loadString(ifs, str, strsize);
        if (tobesave) {
            ptr->push(std::move(str));
        }
        --length;
    }
    if (!tobesave) {
        db.erase(key);
    }
}
void Db::loadHashObject(std::ifstream& ifs, bool tobesave, std::string& key) {
    long length = 0;
    loadInteger(ifs, length);
    auto& ptr = this->db[key];
    if (tobesave) {
        if (!ptr.get()) {
            ptr = SDBObject::CreateHashObject();
        }
    }
    while (length > 0 && ifs.peek() != EOF) {
        std::string hkey, hvalue;
        long strsize = 0;
        loadInteger(ifs, strsize);
        loadString(ifs, hkey, strsize);
        strsize = 0;
        loadInteger(ifs, strsize);
        loadString(ifs, hvalue, strsize);
        if (tobesave) {
            ptr->hadd(std::move(hkey), std::move(hvalue));
        }
        --length;
    }
    if (!tobesave) {
        this->db.erase(key);
    }
}
void Db::loadOsetObject(std::ifstream& ifs, bool tobesave, std::string& key) {
    long length = 0;
    loadInteger(ifs, length);
    auto& ptr = this->db[key];
    if (tobesave) {
        if (!ptr.get()) {
            ptr = SDBObject::CreateOSetObject();
        }
    }
    while (length > 0 && ifs.peek() != EOF) {
        long strsize = 0;
        std::string score, value;
        loadInteger(ifs, strsize);
        loadString(ifs, score, strsize);
        strsize = 0;
        loadInteger(ifs, strsize);
        loadString(ifs, value, strsize);
        if (tobesave) {
            ptr->oadd(StringUtil::toDouble(score), std::move(value));
        }
        --length;
    }
    if (!tobesave) {
        db.erase(key);
    }
}
void Db::loadSetObject(std::ifstream& ifs, bool tobesave, std::string& key) {
    long length = 0;
    loadInteger(ifs, length);
    auto& ptr = this->db[key];
    if (tobesave) {
        if (!ptr.get()) {
            ptr = SDBObject::CreateSetObject();
        }
    }
    while (length > 0 && ifs.peek() != EOF) {
        long strsize = 0;
        std::string str;
        loadInteger(ifs, strsize);
        loadString(ifs, str, strsize);
        if (tobesave) {
            ptr->sadd(std::move(str));
        }
        --length;
    }
    if (!tobesave) {
        db.erase(key);
    }
}
void Db::load(const std::string& filename) {
    std::ifstream ifs(filename, std::ifstream::in | std::ifstream::binary);
    if (!ifs.good()) {
        throw SdbException("no such file!");
    }
    std::string sdb;
    loadString(ifs, sdb, 3);
    if (sdb.compare("SDB") != 0) {
        throw SdbException("file format is not correct!");
    }
    int dbversion = 0;
    loadInteger(ifs, dbversion);
    while (ifs.peek() != EOF) {
        char typeOrExpire = 0;
        bool hasExpire = false;
        bool tobesave = true;
        loadInteger(ifs, typeOrExpire);
        long etime = 0;
        if (typeOrExpire == EXPIRE) {
            loadInteger(ifs, etime);
            if (std::chrono::system_clock::now() <
                std::chrono::system_clock::from_time_t(etime)) {
                hasExpire = true;
            } else {
                tobesave = false;
            }
            loadInteger(ifs, typeOrExpire);
        }
        std::string key;
        long keysize = 0;
        loadInteger(ifs, keysize);
        loadString(ifs, key, keysize);
        if (hasExpire) {
            this->expire.insert({key, etime});
        }
        switch (static_cast<SdbObjType>(typeOrExpire)) {
            case SdbObjType::SDB_STRING:
                loadStringObject(ifs, tobesave, key);
                break;
            case SdbObjType::SDB_LIST:
                loadListObject(ifs, tobesave, key);
                break;
            case SdbObjType::SDB_SET:
                loadSetObject(ifs, tobesave, key);
                break;
            case SdbObjType::SDB_OSET:
                loadOsetObject(ifs, tobesave, key);
                break;
            case SdbObjType::SDB_HASH:
                loadHashObject(ifs, tobesave, key);
                break;
            default:
                throw SdbException("file is not correct!");
                break;
        }
    }
}

}  // namespace SDB
#endif<file_sep>/core/Exception.hpp
#ifndef SDB_EXCEPTIONS_HPP
#define SDB_EXCEPTIONS_HPP
#include <stdexcept>
namespace SDB {

class SdbException : public std::runtime_error {
   public:
    explicit SdbException(const std::string& msg) : std::runtime_error(msg) {}
};

}  // namespace SDB
#endif<file_sep>/executor/DoSet.hpp
#ifndef SDB_DOSET_HPP
#define SDB_DOSET_HPP
#include "../core/Db.hpp"
#include "../core/Exception.hpp"
namespace SDB {
// declare
void doSadd(std::vector<std::string>&, std::ostream&, Db&);
void doSdel(std::vector<std::string>&, std::ostream&, Db&);
void doSgetall(std::vector<std::string>&, std::ostream&, Db&);
// define

void doSadd(std::vector<std::string>& commands, std::ostream& out, Db& db) {
    if (commands.size() < 3) {
        throw SdbException("missing arguments!");
    }
    SDBObject::ObjPointer fres = db.find(commands[1]);
    if (fres.get()) {
        fres->sadd(std::move(commands[2]));
        return;
    }
    auto& ptr = db[std::move(commands[1])];
    if (!ptr.get()) {
        ptr = SDBObject::CreateSetObject();
    }
    ptr->sadd(std::move(commands[2]));
}
void doSdel(std::vector<std::string>& commands, std::ostream& out, Db& db) {
    if (commands.size() < 3) {
        throw SdbException("missing arguments!");
    }
    auto ptr = db.find(commands[1]);
    if (ptr.get()) {
        ptr->sdel(std::move(commands[2]));
    } else {
        out << "null" << '\n';
    }
}
void doSgetall(std::vector<std::string>& commands, std::ostream& out, Db& db) {
    if (commands.size() < 2) {
        throw SdbException("missing arguments!");
    }
    auto ptr = db.find(commands[1]);
    if (ptr.get()) {
        ptr->print(out);
    } else {
        out << "null" << '\n';
    }
}
}  // namespace SDB
#endif<file_sep>/core/Set.hpp
#ifndef SDB_SET_HPP
#define SDB_SET_HPP
#include <unordered_set>
namespace SDB {
//无序集合
template <typename Key, typename Hash = std::hash<Key>,
          typename Equal = std::equal_to<Key>>
class Set {
    friend class Db;

   private:
    std::unordered_set<Key, Hash, Equal> uset;

   public:
    void add(const Key&);
    void add(Key&&);
    void del(const Key&);
    ssize_t size() { return uset.size(); }
    void print(std::ostream&);
};
template <typename Key, typename Hash, typename Equal>
void Set<Key, Hash, Equal>::add(const Key& value) {
    uset.insert(value);
}
template <typename Key, typename Hash, typename Equal>
void Set<Key, Hash, Equal>::add(Key&& value) {
    uset.insert(std::move(value));
}
template <typename Key, typename Hash, typename Equal>
void Set<Key, Hash, Equal>::del(const Key& value) {
    uset.erase(value);
}

// 将值输出到指定输出流中
template <typename Key, typename Hash, typename Equal>
void Set<Key, Hash, Equal>::print(std::ostream& out) {
    for (auto it = uset.begin(); it != uset.end(); ++it) {
        out << *it << '\n';
    }
}

}  // namespace SDB
#endif<file_sep>/Makefile
CLIENTDIR=./client
SERVERDIR=./server
default:
	make all
sdbclient:
	make -C client
sdbserver:
	make -C server
sdbconsole:
	make -C console
all:
	make sdbclient
	make sdbserver
clean:
	rm ./client/sdbclient ./server/sdbserver<file_sep>/server/SdbServer.hpp
#ifndef SDB_SDBSERVER_HPP
#define SDB_SEBSERVER_HPP
#include <iostream>
#include <sstream>
#include "../core/Exception.hpp"
#include "../executor/Executor.hpp"
#include "../net/TcpServer.hpp"
namespace SDB {

class SdbServer : public Net::TcpServer {
   private:
    Executor executor;

   public:
    virtual void onMessageCallback(std::shared_ptr<Net::TcpConnection>);
    virtual void onCloseCallback(std::shared_ptr<Net::TcpConnection>);
    virtual void onConnectedCallback(std::shared_ptr<Net::TcpConnection>);
    virtual void onWriteCallback(std::shared_ptr<Net::TcpConnection>);

    SdbServer(Net::InetAddress& addr) : TcpServer(addr) {}
};

void SdbServer::onMessageCallback(std::shared_ptr<Net::TcpConnection> conn) {
    if (conn->getReadBuf().size() < (sizeof(size_t) + 3)) {
        return;
    }
    std::string res = conn->getReadBuf().copyBufferAsString(sizeof(size_t) + 3);
    size_t len = 0;
    char* pos = reinterpret_cast<char*>(&len);
    for (unsigned int i = 0; i < sizeof(size_t); ++i) {
        pos[i] = res[3 + i];
    }
    std::cout << "get len:" << len << std::endl;
    if (conn->getReadBuf().size() >= len) {
        res = conn->getReadBuf().readBufferAsString();
        std::string input(res, sizeof(size_t) + 3);
        std::cout << input << std::endl;
        std::stringbuf sbuf;
        std::ostream out(&sbuf);
        try {
            executor.execute(input, out);
        } catch (const SdbException& e) {
            out << e.what() << std::endl;
        }
        std::string reply = "sdb";
        std::string content = sbuf.str();
        std::cout << "size:" << content.size() << "content:" << content
                  << std::endl;
        size_t len = reply.size() + content.size() + sizeof(len);
        reply.append(reinterpret_cast<char*>(&len), sizeof(len));
        reply.append(content);
        std::cout << "reply size:" << reply.size() << "reply:" << reply
                  << std::endl;
        conn->writeToBuf(reply);
        conn->getEvent()->setWriteable();
        modifyEvent(*conn->getEvent());
    }
}
void SdbServer::onCloseCallback(std::shared_ptr<Net::TcpConnection> conn) {
    //
    std::cout << "close:" << conn->getPeerAddress().getIp() + ":"
              << conn->getPeerAddress().getPort() << std::endl;
}
void SdbServer::onConnectedCallback(std::shared_ptr<Net::TcpConnection> conn) {
    //
    std::cout << "connected:" << conn->getPeerAddress().getIp() + ":"
              << conn->getPeerAddress().getPort() << std::endl;
}

void SdbServer::onWriteCallback(std::shared_ptr<Net::TcpConnection> conn) {
    if (conn->getWriteBuf().size() == 0) {
        // conn->shutdown();
        std::cout << conn->getPeerAddress().getIp() << ":"
                  << conn->getPeerAddress().getPort() << " write completed!"
                  << std::endl;
        conn->getEvent()->unsetWriteable();
        modifyEvent(*conn->getEvent());
    }
}

}  // namespace SDB

#endif<file_sep>/client/sdbclient.cpp
#include "SdbClient.hpp"
#include <iostream>
using namespace SDB;
int main() {
    Net::InetAddress addr = Net::InetAddress::IPV4AddressAny();
    addr.setPort(5481);
    SdbClient client(addr);
    std::string input;
    std::string res;
    while (1) {
        std::cout << "sdb>>";
        std::getline(std::cin, input);
        if (input == "exit") {
            std::cout << "bye bye!" << std::endl;
            input.clear();
            //client
            break;
        } else if (input.size() == 0) {
            continue;
        }
        try {
            client.send(input);
            res = client.read();
            std::cout << res;
        } catch (const std::exception& e) {
            std::cout << e.what() << std::endl;
        }
    }
}<file_sep>/net/TcpSocket.hpp
#ifndef NET_TCPSOCKET_HPP
#define NET_TCPSOCKET_HPP
#include "Socket.hpp"
namespace Net {

class TcpSocket : public Socket {
   private:
    InetAddress peerAddress;

   public:
    void setTcpNoDelay(bool);
    bool listen(int);
    int accept(InetAddress&);
    bool connect(InetAddress&);
    bool connect(const std::string&, int);
    size_t read(char*, size_t);
    size_t write(const char*, size_t);
    InetAddress getPeerAddress() const { return peerAddress; }
};

void TcpSocket::setTcpNoDelay(bool enable) {
    int opt = enable ? 1 : 0;
    ::setsockopt(this->getfd(), IPPROTO_TCP, TCP_NODELAY, &opt, sizeof(opt));
}

bool TcpSocket::listen(int backlog = 1) {
    if (this->getfd() == -1) return false;
    return (::listen(this->getfd(), backlog) == 0);
}

int TcpSocket::accept(InetAddress& address) {
    socklen_t len;
    if (getDomain() == NetDomain::NetInet) {
        len = sizeof(address.addr.addr4);
        return ::accept(getfd(), (struct sockaddr*)&address.addr.addr4, &len);
    } else if (getDomain() == NetDomain::NetInet6) {
        len = sizeof(address.addr.addr6);
        return ::accept(getfd(), (struct sockaddr*)&address.addr.addr6, &len);
    } else {
        return -1;
    }
}

bool TcpSocket::connect(InetAddress& address) {
    if (getfd() == -1) {
        if (!create(address.dm, NetSocketType::NetSocketStream,
                    address.protocol)) {
            return false;
        }
    }
    if (address.dm == NetDomain::NetInet) {
        if ((::connect(this->getfd(), (struct sockaddr*)&address.addr.addr4,
                       sizeof(address.addr.addr4)) == 0)) {
            peerAddress = address;
            return true;
        }
    } else if (address.dm == NetDomain::NetInet6) {
        if ((::connect(this->getfd(), (struct sockaddr*)&address.addr.addr6,
                       sizeof(address.addr.addr6)) == 0)) {
            peerAddress = address;
            return true;
        }
    }
    closefd();
    return false;
}

bool TcpSocket::connect(const std::string& dm_ip, int port = 80) {
    auto address = InetAddress::getHostAddresses(dm_ip);
    for (auto it = address.begin(); it != address.end(); ++it) {
        it->setPort(port);
        if (connect(*it)) {
            return true;
        }
    }
    return false;
}

size_t TcpSocket::read(char* buf, size_t s) {
    //
    return Socket::read(buf, s);
}
size_t TcpSocket::write(const char* buf, size_t s) {
    //
    return Socket::write(buf, s);
}

}  // namespace Net
#endif<file_sep>/net/Event.hpp
#ifndef SDB_EVENT_HPP
#define SDB_EVENT_HPP
#include <chrono>
namespace Net {

class EventStatus final {
   public:
    static const int EVENT_NONE = 0;
    static const int EVENT_READABLE = 1;
    static const int EVENT_WRITEABLE = 2;
    static const int EVENT_BARRIER = 4;
    EventStatus() = delete;
};

class EventType final {
   public:
    static const int FILE_EVENT = 1;
    static const int TIME_EVENT = 2;
    static const int ALL_EVENTS = FILE_EVENT | TIME_EVENT;
    static const int EVENT_DONT_WAIT = 4;
    static const int EVENT_CALL_AFTER_SLEEP = 8;
    EventType() = delete;
};

class TimeEventIdStatus final {
   public:
    static const long TIMEEVENT_DELETED_ID = -1;
    TimeEventIdStatus() = delete;
};

}  // namespace Net
#endif<file_sep>/console/Console.hpp
#ifndef SDB_CONSOLE_HPP
#define SDB_CONSOLE_HPP
#include <iostream>
#include <string>
#include "../core/Exception.hpp"
#include "../executor/Executor.hpp"

namespace SDB {
class Console {
   private:
    std::string input;

   public:
    void run();
};
void Console::run() {
    Executor executor;
    while (true) {
        std::cout << "sdb>>";
        std::getline(std::cin, input);
        if (input == "exit") {
            std::cout << "bye bye!" << std::endl;
            break;
        }
        try {
            executor.execute(input, std::cout);
            std::cout << std::flush;
        } catch (const SdbException& e) {
            std::cout << e.what() << std::endl;
        }
    }
}
}  // namespace SDB
#endif<file_sep>/net/unix_header.h
#ifndef UNIX_NET_LIB_HEADER_H
#define UNIX_NET_LIB_HEADER_H
extern "C" {
#include <arpa/inet.h>
#include <fcntl.h>
#include <ifaddrs.h>
#include <linux/tcp.h>
#include <net/if.h>
#include <netdb.h>
#include <netinet/in.h>
#include <sys/epoll.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>
}
#endif<file_sep>/executor/DoHash.hpp
#ifndef SDB_HASH_HPP
#define SDB_HASH_HPP
#include "../core/Db.hpp"
#include "../core/Exception.hpp"
namespace SDB {
// declare
void doHadd(std::vector<std::string>&, std::ostream&, Db&);
void doHdel(std::vector<std::string>&, std::ostream&, Db&);
void doHgetall(std::vector<std::string>&, std::ostream&, Db&);
// define
void doHadd(std::vector<std::string>& commands, std::ostream& out, Db& db) {
    if (commands.size() < 4) {
        throw SdbException("missing arguments!");
    }
    SDBObject::ObjPointer fres = db.find(commands[1]);
    if (fres.get()) {
        fres->hadd(std::move(commands[2]), std::move(commands[3]));
        return;
    }
    auto& ptr = db[std::move(commands[1])];
    if (!ptr.get()) {
        ptr = SDBObject::CreateHashObject();
    }
    ptr->hadd(std::move(commands[2]), std::move(commands[3]));
}

void doHdel(std::vector<std::string>& commands, std::ostream& out, Db& db) {
    if (commands.size() < 3) {
        throw SdbException("missing arguments!");
    }
    auto ptr = db.find(commands[1]);
    if (ptr.get()) {
        ptr->hdel(commands[2]);
    } else {
        out << "null" << '\n';
    }
}
void doHgetall(std::vector<std::string>& commands, std::ostream& out, Db& db) {
    if (commands.size() < 2) {
        throw SdbException("missing arguments!");
    }
    auto ptr = db.find(commands[1]);
    if (ptr.get()) {
        ptr->print(out);
    } else {
        out << "null" << '\n';
    }
}
}  // namespace SDB
#endif<file_sep>/executor/DoString.hpp
#ifndef SDB_DOSTRING_HPP
#define SDB_DOSTRING_HPP
#include "../core/Db.hpp"
#include "../core/Exception.hpp"
namespace SDB {
// declare
void doSset(std::vector<std::string>&, std::ostream&, Db&);
void doSget(std::vector<std::string>&, std::ostream&, Db&);
void doStrlen(std::vector<std::string>&, std::ostream&, Db&);
void doAppend(std::vector<std::string>&, std::ostream&, Db&);
void doGetRange(std::vector<std::string>&, std::ostream&, Db&);
// define

void doSset(std::vector<std::string>& commands, std::ostream& out, Db& db) {
    if (commands.size() < 3) {
        throw SdbException("error:missing arguments!");
    } else {
        SDBObject::ObjPointer fres = db.find(commands[1]);
        if (fres.get()) {
            fres->set(std::move(commands[2]));
            return;
        }
        SDBObject::ObjPointer& newptr = db[commands[1]];
        if (!newptr.get()) {
            newptr = SDBObject::CreateStrObject();
        }
        newptr->set(std::move(commands[2]));
    }
}

void doSget(std::vector<std::string>& commands, std::ostream& out, Db& db) {
    if (commands.size() < 2) {
        throw SdbException("error:missing arguments!");
    } else {
        SDBObject::ObjPointer res = db.find(commands[1]);
        if (res.get()) {
            res->print(out);
        } else {
            out << "null" << '\n';
        }
    }
}

void doAppend(std::vector<std::string>& commands, std::ostream& out, Db& db) {
    if (commands.size() < 3) {
        throw SdbException("error:missing arguments!");
    } else {
        SDBObject::ObjPointer fres = db.find(commands[1]);
        if (fres.get()) {
            fres->append(commands[2]);
            return;
        }
        SDBObject::ObjPointer& res = db[std::move(commands[1])];
        if (!res.get()) {
            res = SDBObject::CreateStrObject();
        }
        res->append(commands[2]);
    }
}

void doStrlen(std::vector<std::string>& commands, std::ostream& out, Db& db) {
    if (commands.size() < 2) {
        throw SdbException("error:missing arguments!");
    }
    SDBObject::ObjPointer res = db.find(commands[1]);
    if (res.get()) {
        out << std::to_string(res->getStrLen()) << '\n';
    } else {
        out << "0" << '\n';
    }
}

void doGetRange(std::vector<std::string>& commands, std::ostream& out, Db& db) {
    if (commands.size() < 4) {
        throw SdbException("error:missing arguments!");
    }
    SDBObject::ObjPointer res = db.find(commands[1]);
    if (res.get()) {
        int start = StringUtil::toDouble(commands[2]);
        int end = StringUtil::toDouble(commands[3]);
        std::string temp = res->getRange(start, end);
        if (temp.size() > 0)
            out << temp << '\n';
        else
            out << "\"\"" << '\n';
    } else {
        out << "null" << '\n';
    }
}

}  // namespace SDB
#endif<file_sep>/net/TcpConnection.hpp
#ifndef NET_TCPCONNECTION_HPP
#define NET_TCPCONNECTION_HPP
#include <memory>
#include "Buffer.hpp"
#include "IOEvent.hpp"
#include "InetAddress.hpp"
namespace Net {

class TcpConnection : public std::enable_shared_from_this<TcpConnection> {
   public:
    using DefaultCallback = std::function<void(std::shared_ptr<TcpConnection>)>;

   private:
    IOEventPtr event;
    InetAddress peerAddress;
    Buffer readbuf;
    Buffer writebuf;
    DefaultCallback onMessageCallback;
    DefaultCallback onCloseCallback;
    DefaultCallback onErrorCallback;
    DefaultCallback onWriteCallback;
    DefaultCallback notifyClose;

   public:
    TcpConnection(int fd, InetAddress& peer) : peerAddress(peer) {
        event = std::make_shared<IOEvent>(fd);
        event->setReadFunc(std::bind(&TcpConnection::read, this));
        event->setWriteFunc(std::bind(&TcpConnection::write, this));
        event->setCloseFunc(std::bind(&TcpConnection::closeConnection, this));
        event->setReadable();
    }
    void read();
    void write();
    void closeConnection();
    void readFromBuf();
    void writeToBuf(const std::string& msg);
    Buffer& getReadBuf() { return readbuf; }
    Buffer& getWriteBuf() { return writebuf; }
    void setOnMessageCallback(const DefaultCallback&);
    void setOnCloseCallback(const DefaultCallback&);
    void setOnErrorCallback(const DefaultCallback&);
    void setOnWriteCallback(const DefaultCallback&);
    void setNotifyClose(const DefaultCallback&);
    std::string&& readBufferAsString() { return readbuf.readBufferAsString(); }
    InetAddress getPeerAddress() { return peerAddress; }
    IOEventPtr& getEvent() { return event; }
};

void TcpConnection::read() {
    //
    if (readbuf.readFromFd(event->getfd()) > 0) {
        if (onMessageCallback) {
            onMessageCallback(shared_from_this());
        }
    } else {
        if (notifyClose) {
            notifyClose(shared_from_this());
        }
        if (onCloseCallback) {
            onCloseCallback(shared_from_this());
        }
    }
}

void TcpConnection::write() {
    //
    if (writebuf.size() == 0) {
        return;
    }
    if (writebuf.writeToFd(event->getfd()) > 0) {
        if (onWriteCallback) {
            onWriteCallback(shared_from_this());
        }
    } else {
        if (notifyClose) {
            notifyClose(shared_from_this());
        }
        if (onCloseCallback) {
            onCloseCallback(shared_from_this());
        }
    }
}

void TcpConnection::closeConnection() {
    if (notifyClose) {
        notifyClose(shared_from_this());
    }
    if (onCloseCallback) {
        onCloseCallback(shared_from_this());
    }
}

void TcpConnection::writeToBuf(const std::string& msg) {
    // add
    writebuf.push(msg);
}

void TcpConnection::setOnMessageCallback(const DefaultCallback& mc) {
    //
    onMessageCallback = mc;
}

void TcpConnection::setOnCloseCallback(const DefaultCallback& cc) {
    //
    onCloseCallback = cc;
}

void TcpConnection::setOnErrorCallback(const DefaultCallback& ec) {
    //
    onErrorCallback = ec;
}

void TcpConnection::setOnWriteCallback(const DefaultCallback& wc) {
    //
    onWriteCallback = wc;
}

void TcpConnection::setNotifyClose(const DefaultCallback& nc) {
    //
    notifyClose = nc;
}

using TcpConnectionPtr = std::shared_ptr<TcpConnection>;

}  // namespace Net
#endif<file_sep>/net/InetAddress.hpp
#ifndef NET_NETADDRESS_HPP
#define NET_NETADDRESS_HPP
#include <string.h>
#include <string>
#include <vector>
#include "NetLib.hpp"
namespace Net {

class InetAddress {
    friend class Socket;
    friend class TcpSocket;

   private:
    union {
        struct sockaddr_in addr4;
        struct sockaddr_in6 addr6;
    } addr;
    NetDomain dm;
    NetSocketType socktype;
    int protocol;

   public:
    InetAddress();
    InetAddress(int, NetDomain);
    InetAddress(struct sockaddr_in&);
    InetAddress(struct sockaddr_in6&);
    static InetAddress IPV6AddressAny();
    static InetAddress IPV4AddressAny();
    static std::vector<std::string> getHostIPAddresses(const std::string&,
                                                       Net::NetDomain);
    static std::vector<InetAddress> getLocalAddresses(NetDomain);
    static std::vector<InetAddress> getHostAddresses(const std::string&);
    static std::string localHostName();
    InetAddress& operator=(const InetAddress&);
    InetAddress(const InetAddress&);
    void setPort(const uint16_t port_num);
    std::string getIp() const;
    int getPort() const;
    NetDomain getDomain() const { return dm; }
    NetSocketType getSocketType() const { return socktype; }
    int getProtocol() const { return protocol; }
};

InetAddress::InetAddress() {
    ::memset((void*)&addr, 0, sizeof(addr));
    dm = NetDomain::NetInet;
    socktype = NetSocketType::NetSocketStream;
    protocol = 0;
}

InetAddress::InetAddress(int port, NetDomain domain = NetDomain::NetInet) {
    ::memset((void*)&addr, 0, sizeof(addr));
    if (domain == NetDomain::NetInet) {
        addr.addr4.sin_family = AF_INET;
        addr.addr4.sin_addr.s_addr = htonl(INADDR_ANY);
        addr.addr4.sin_port = ::htons(port);
    } else {
        addr.addr6.sin6_family = AF_INET6;
        addr.addr6.sin6_addr = in6addr_any;
        addr.addr6.sin6_port = ::htons(port);
    }
    dm = domain;
    socktype = NetSocketType::NetSocketStream;
    protocol = 0;
}

InetAddress::InetAddress(struct sockaddr_in& addr4) {
    ::memcpy((void*)&this->addr.addr4, (void*)&addr4,
             sizeof(struct sockaddr_in));
    dm = NetDomain::NetInet;
    socktype = NetSocketType::NetSocketStream;
    protocol = 0;
}

InetAddress::InetAddress(struct sockaddr_in6& addr6) {
    ::memcpy((void*)&this->addr.addr6, (void*)&addr6,
             sizeof(struct sockaddr_in6));
    dm = NetDomain::NetInet6;
    socktype = NetSocketType::NetSocketStream;
    protocol = 0;
}

InetAddress InetAddress::IPV6AddressAny() {
    return InetAddress(0, NetDomain::NetInet6);
}

InetAddress InetAddress::IPV4AddressAny() {
    return InetAddress(0, NetDomain::NetInet);
}

InetAddress& InetAddress::operator=(const InetAddress& lv) {
    ::memcpy((void*)&this->addr, (void*)&lv.addr, sizeof(addr));
    dm = lv.dm;
}

InetAddress::InetAddress(const InetAddress& lv) {
    ::memcpy((void*)&this->addr, (void*)&lv.addr, sizeof(addr));
    dm = lv.dm;
}

void InetAddress::setPort(const uint16_t port_num) {
    if (dm == NetDomain::NetInet) {
        addr.addr4.sin_port = ::htons(port_num);
    } else {
        addr.addr6.sin6_port = ::htons(port_num);
    }
}

std::vector<std::string> InetAddress::getHostIPAddresses(
    const std::string& hostname, NetDomain domain = NetDomain::NetUnspecific) {
    char hname[ADDRSTRLEN];
    std::vector<std::string> res;
    struct addrinfo hints, *resinfo;
    if (hostname.size() == 0) {
        return res;
    }
    ::bzero(&hints, sizeof(struct addrinfo));
    if (domain == NetDomain::NetInet) {
        hints.ai_family = AF_INET;
    } else if (domain == NetDomain::NetInet6) {
        hints.ai_family = AF_INET6;
    } else {
        hints.ai_family = AF_UNSPEC;
    }
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_flags = AI_PASSIVE;
    hints.ai_protocol = 0;
    if (::getaddrinfo(hostname.c_str(), NULL, &hints, &resinfo) != 0) {
        return res;
    }
    char str[ADDRSTRLEN];
    auto ptr = resinfo;
    while (resinfo != nullptr) {
        if (resinfo->ai_family == AF_INET) {
            struct sockaddr_in* addr = (struct sockaddr_in*)resinfo->ai_addr;
            if (::inet_ntop(resinfo->ai_family, &addr->sin_addr, str,
                            ADDRSTRLEN)) {
                res.push_back(std::string(str));
            }
        } else {
            struct sockaddr_in6* addr6 = (struct sockaddr_in6*)resinfo->ai_addr;
            if (::inet_ntop(resinfo->ai_family, &addr6->sin6_addr, str,
                            ADDRSTRLEN)) {
                res.push_back(std::string(str));
            }
        }
        resinfo = resinfo->ai_next;
    }
    ::freeaddrinfo(ptr);
    return res;
}

std::vector<InetAddress> InetAddress::getHostAddresses(
    const std::string& hostname) {
    char hname[ADDRSTRLEN];
    std::vector<InetAddress> res;
    res.reserve(10);
    struct addrinfo hints, *resinfo;
    if (hostname.size() == 0) {
        return res;
    }
    ::bzero(&hints, sizeof(struct addrinfo));
    hints.ai_family = AF_UNSPEC;
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_flags = AI_CANONNAME;
    hints.ai_protocol = 0;
    if (::getaddrinfo(hostname.c_str(), NULL, &hints, &resinfo) != 0) {
        return res;
    }
    char str[ADDRSTRLEN];
    auto ptr = resinfo;
    while (resinfo != nullptr) {
        NetSocketType type = static_cast<NetSocketType>(resinfo->ai_socktype);
        if (resinfo->ai_family == AF_INET) {
            struct sockaddr_in* addr = (struct sockaddr_in*)resinfo->ai_addr;
            InetAddress tmadd(*addr);
            tmadd.protocol = resinfo->ai_protocol;
            tmadd.socktype = static_cast<NetSocketType>(resinfo->ai_socktype);
            res.push_back(tmadd);
        } else {
            struct sockaddr_in6* addr6 = (struct sockaddr_in6*)resinfo->ai_addr;
            InetAddress tmadd(*addr6);
            tmadd.protocol = resinfo->ai_protocol;
            tmadd.socktype = static_cast<NetSocketType>(resinfo->ai_socktype);
            res.push_back(tmadd);
        }
        resinfo = resinfo->ai_next;
    }
    ::freeaddrinfo(ptr);
    return res;
}

std::vector<InetAddress> InetAddress::getLocalAddresses(
    NetDomain domain = NetDomain::NetUnspecific) {
    std::vector<InetAddress> res;
    struct ifaddrs *ifap, *temp;
    char str[ADDRSTRLEN];
    if (::getifaddrs(&ifap) != 0) {
        return res;
    }
    int family = 0;
    if (domain == NetDomain::NetInet) {
        family = AF_INET;
    } else if (domain == NetDomain::NetInet6) {
        family = AF_INET6;
    } else {
        family = AF_UNSPEC;
    }
    for (temp = ifap; temp != nullptr; temp = temp->ifa_next) {
        if (family == AF_UNSPEC || temp->ifa_addr->sa_family == family) {
            if (temp->ifa_addr->sa_family == AF_INET6) {
                struct sockaddr_in6* addr6 =
                    (struct sockaddr_in6*)temp->ifa_addr;
                res.push_back(InetAddress(*addr6));
            } else if (temp->ifa_addr->sa_family == AF_INET) {
                struct sockaddr_in* addr4 = (struct sockaddr_in*)temp->ifa_addr;
                res.push_back(InetAddress(*addr4));
            }
        }
    }
    ::freeifaddrs(ifap);
    return res;
}

std::string InetAddress::localHostName() {
    char hostname[ADDRSTRLEN];
    if (::gethostname(hostname, ADDRSTRLEN) != 0) {
        return "";
    }
    return std::string(hostname);
}

std::string InetAddress::getIp() const {
    if (dm == NetDomain::NetInet) {
        return std::string(::inet_ntoa(*(struct in_addr*)&addr.addr4.sin_addr));
    } else {
        char str[ADDRSTRLEN];
        if (::inet_ntop(addr.addr6.sin6_family, &addr.addr6.sin6_addr, str,
                        ADDRSTRLEN)) {
            return std::string(str);
        } else {
            return std::string("");
        }
    }
}

int InetAddress::getPort() const {
    // return port
    if (dm == NetDomain::NetInet) {
        return ::ntohs(addr.addr4.sin_port);
    } else {
        return ::ntohs(addr.addr6.sin6_port);
    }
}

}  // namespace Net

#endif

languages: Makefile Markdown C++ C

repo_language: Makefile

repo_name: longzuo/SimpleKeyValueDb