* Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain a copy of Mulan PSL v2 at:
* http://license.coscl.org.cn/MulanPSL2
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
* See the Mulan PSL v2 for more details.
*/
#ifndef BOOST_SS_KEY_H
#define BOOST_SS_KEY_H
#include <iomanip>
#include "binary/byte_buffer.h"
#include "common/util/key_group_util.h"
#include "include/binary_data.h"
#include "state_id.h"
namespace ock {
namespace bss {
struct KeyNode {
public:
KeyNode() = default;
void Init(uint32_t hashCode, const uint8_t *keyData, uint32_t keyLen)
{
mHashCode = hashCode;
mKeyLen = keyLen;
mKeyData = keyData;
}
KeyNode(uint32_t hashCode, uint32_t keyLen, const uint8_t *keyData)
: mHashCode(hashCode), mKeyLen(keyLen), mKeyData(keyData)
{
}
inline uint32_t HashCode() const
{
return mHashCode;
}
inline uint32_t KeyLen() const
{
return mKeyLen;
}
inline const uint8_t *KeyData() const
{
return mKeyData;
}
inline void HashCode(uint32_t hashCode)
{
mHashCode = hashCode;
}
inline void KeyLen(uint32_t keyLen)
{
mKeyLen = keyLen;
}
inline void KeyData(const uint8_t *keyData)
{
mKeyData = keyData;
}
template <typename KN> inline int32_t CompareKeyNode(const KN &other) const
{
int32_t cmp = BssMath::IntegerCompare(mHashCode, other.mHashCode);
return (cmp != 0) ? cmp :
((cmp = memcmp(mKeyData, other.mKeyData, std::min(mKeyLen, other.mKeyLen))) != 0) ?
cmp :
BssMath::IntegerCompare(mKeyLen, other.mKeyLen);
}
protected:
* for primary key, hashcode is KeyHashCode ^ NameSpaceHashCode
* for secondary key, hashcode is calculate by HashCode::Hash(SecondaryKeyData, SecondaryKeyLen);
*/
uint32_t mHashCode{ 0 };
uint32_t mKeyLen{ 0 };
const uint8_t *mKeyData{ nullptr };
};
struct PriKeyNode : public KeyNode {
public:
PriKeyNode() = default;
PriKeyNode(uint16_t stateId, uint32_t hashCode, const uint8_t *keyData, uint32_t keyLen)
: KeyNode(hashCode, keyLen, keyData), mStateId(stateId)
{
}
void Init(uint16_t stateId, uint32_t hashCode, const uint8_t *keyData, uint32_t keyLen)
{
KeyNode::Init(hashCode, keyData, keyLen);
mStateId = stateId;
}
inline uint32_t KeyHashCode() const
{
return mHashCode ^ NsHashCode();
}
* Is primary key node the same with other or not?
* @param other other primary key node.
* @return return true if equals, else return false.
*/
inline bool operator==(const PriKeyNode &other) const
{
return CompareStateIdFirst(other) == 0;
}
* Get state id.
* @return state id.
*/
inline uint16_t StateId() const
{
return mStateId;
}
* Set state id.
* @param stateId state id.
*/
inline void StateId(uint16_t stateId)
{
mStateId = stateId;
}
* Get namespace hash code, if there is a namespace, hashCode is stored in the first 4 bytes of the main key.
* @return return namespace hash code.
*/
inline uint32_t NsHashCode() const
{
return StateId::HasNameSpace(mStateId) ? *reinterpret_cast<const uint32_t *>(mKeyData) : 0;
}
* Compare primary key, compare state id first.
* @param other other key.
* @return If the current key is less than other, return -1. When they are equal, return 0.
* If the current key is greater than other, return 1.
*/
inline int32_t CompareStateIdFirst(const PriKeyNode &other) const
{
int32_t cmp = BssMath::IntegerCompare(mStateId, other.mStateId);
return (cmp != 0) ? cmp : ComparePriKeyNode(other);
}
inline int32_t ComparePriKeyNode(const PriKeyNode &other) const
{
int32_t cmp = BssMath::IntegerCompare(KeyHashCode(), other.KeyHashCode());
return (cmp != 0) ? cmp :
((cmp = memcmp(mKeyData, other.mKeyData, std::min(mKeyLen, other.mKeyLen))) != 0) ?
cmp :
BssMath::IntegerCompare(mKeyLen, other.mKeyLen);
}
* Compare primary key, compare state id last.
* @param other other key.
* @return If the current key is less than other, return -1. When they are equal, return 0.
* If the current key is greater than other, return 1.
*/
inline int32_t CompareStateIdLast(const PriKeyNode &other) const
{
int32_t cmp = ComparePriKeyNode(other);
return (cmp != 0) ? cmp : BssMath::IntegerCompare(mStateId, other.mStateId);
}
* If it is a SUBMAP/SUBLIST type State, the primary keydata will contain the namespace, the memory struct of
* KeyData is : | NsHashCode(4Bytes) | KeyRealData(NBytes) | NsData(NBytes) | KeyRealLength(4Bytes)
* first 4 bytes is namespace hashcode, last 4 bytes is KeyRealData length.
*/
* Get real key data, exclude namespace.
*/
inline const uint8_t *RealKeyData() const
{
return StateId::HasNameSpace(mStateId) ? const_cast<uint8_t *>(mKeyData + NO_4) : mKeyData;
}
* Get real key length, exclude namespace.
* @return real key length exclude namespace.
*/
inline uint32_t RealKeyLen() const
{
return StateId::HasNameSpace(mStateId) ? *reinterpret_cast<const uint32_t *>(mKeyData + mKeyLen - NO_4) :
mKeyLen;
}
* There are the same namespace or not?
* @param nameSpace name space string.
* @param nameSpaceLen name space length.
* @return return true if the same, else return false.
*/
inline bool HasSameNameSpace(const uint8_t *nameSpace, uint32_t nameSpaceLen) const
{
if (UNLIKELY(!StateId::HasNameSpace(mStateId))) {
return false;
}
if (UNLIKELY(nameSpace == nullptr)) {
return false;
}
uint32_t thisKeyRealLen = *reinterpret_cast<const uint32_t *>(mKeyData + mKeyLen - NO_4);
uint32_t thisNameSpaceLen = mKeyLen - NO_8 - thisKeyRealLen;
if (thisNameSpaceLen != nameSpaceLen) {
return false;
}
const uint8_t *thisNameSpace = mKeyData + NO_4 + thisKeyRealLen;
return memcmp(thisNameSpace, nameSpace, nameSpaceLen) == 0;
}
* For printing primary key.
* @return primary key string.
*/
inline std::string ToString() const
{
std::ostringstream oss;
oss << "StateId: " << mStateId << ", KeyHashCode: " << KeyHashCode() << ", KeyLen: " << mKeyLen
<< ", KeyData: [";
if (UNLIKELY(mKeyData != nullptr)) {
uint32_t printLen = std::min(NO_20, mKeyLen);
for (uint32_t i = 0; i < printLen; i++) {
oss << std::hex << std::uppercase << std::setw(NO_2) << std::setfill('0')
<< static_cast<int>(mKeyData[i]);
}
} else {
oss << "null";
}
oss << "]";
return oss.str();
}
private:
uint16_t mStateId;
uint16_t mReserved[NO_3]{};
};
using PriKeyNodePtr = PriKeyNode *;
struct SecKeyNode : public KeyNode {
public:
SecKeyNode() = default;
SecKeyNode(uint32_t hashCode, const uint8_t *keyData, uint32_t keyLen) : KeyNode(hashCode, keyLen, keyData)
{
}
* It equals other secondary key or not.
* @return return true if it equals, else return false.
*/
bool operator==(const SecKeyNode &other)
{
return CompareKeyNode(other) == 0;
}
* For printing secondary key.
* @return secondary key string.
*/
inline std::string ToString() const
{
std::ostringstream oss;
oss << "HashCode: " << mHashCode << ", KeyLen: " << mKeyLen << ", KeyData: [";
if (UNLIKELY(mKeyData != nullptr)) {
uint32_t printLen = std::min(NO_20, mKeyLen);
for (uint32_t i = 0; i < printLen; i++) {
oss << std::hex << std::uppercase << std::setw(NO_2) << std::setfill('0')
<< static_cast<int>(mKeyData[i]);
}
} else {
oss << "null";
}
oss << "]";
return oss.str();
}
};
using SecKeyNodePtr = SecKeyNode *;
enum KeyFlag {
NORMAL_KEY_FLAG = 0b00000000,
END_KEY_FLAG = 0b00000001,
PRE_KEY_FLAG = 0b00000010,
};
* NOTICE: Align according to the size of the CPU Cache Line, which is 64 bytes. The key cannot exceed 64 bytes!!!
*/
struct Key;
using KeyPtr = Key *;
using KeyRef = std::shared_ptr<Key>;
struct Key {
public:
Key() = default;
* Initialize key for single key.
* @param priKey single key.
* @param buffer buffer object, store key data for key lifecycle management.
* @param flag key flag, see KeyFlag.
*/
inline void Init(const PriKeyNode &priKey, const BufferRef &buffer, uint8_t flag = 0)
{
uint16_t stateId = priKey.StateId();
mHasSecKey = StateId::HasSecKey(stateId);
mHasNameSpace = StateId::HasNameSpace(stateId);
mPriKey = priKey;
mMixedHashCode = mPriKey.StateId() ^ mPriKey.HashCode();
mFlag = flag;
mBuffer = buffer;
}
* Initialize key for dual key.
* @param priKey primary key.
* @param secKey secondary key.
* @param buffer buffer object, store key data for key lifecycle management.
* @param flag key flag, see KeyFlag.
*/
inline void Init(const PriKeyNode &priKey, const SecKeyNode &secKey, const BufferRef &buffer, uint8_t flag = 0)
{
uint16_t stateId = priKey.StateId();
mHasSecKey = StateId::HasSecKey(stateId);
mHasNameSpace = StateId::HasNameSpace(stateId);
mPriKey = priKey;
mSecKey = mHasSecKey ? secKey : SecKeyNode();
mMixedHashCode = mPriKey.StateId() ^ mPriKey.HashCode() ^ mSecKey.HashCode();
mFlag = flag;
mBuffer = buffer;
}
* Construct key for single key.
* @param priKey single key.
* @param buffer buffer object, store key data for key lifecycle management.
* @param flag key flag, see KeyFlag.
*/
Key(const PriKeyNode &priKey, const BufferRef &buffer, uint8_t flag = 0)
{
Init(priKey, buffer, flag);
}
* Construct key for dual key.
* @param priKey primary key.
* @param secKey secondary key.
* @param buffer buffer object, store key data for key lifecycle management.
* @param flag key flag, see KeyFlag.
*/
Key(const PriKeyNode &priKey, const SecKeyNode &secKey, const BufferRef &buffer, uint8_t flag = 0)
{
Init(priKey, secKey, buffer, flag);
}
* Is equal to other key.
* @param other other key.
* @return return true if equals, else return false.
*/
inline bool operator==(const Key &other)
{
return Equal(other);
}
* Compare with other key.
* @param other other key.
* @return If the current key is less than other, return -1. When they are equal, return 0.
* If the current key is greater than other, return 1.
*/
inline int32_t Compare(const Key &other) const
{
int32_t cmp = ComparePQKey(other);
if (cmp != NO_PQ_DATA) {
return cmp;
}
cmp = mPriKey.ComparePriKeyNode(other.mPriKey);
if (cmp != 0) {
return cmp;
}
cmp = BssMath::IntegerCompare(mPriKey.StateId(), other.StateId());
if (cmp != 0) {
return cmp;
}
if (!mHasSecKey) {
return 0;
}
return mSecKey.CompareKeyNode(other.mSecKey);
}
inline int32_t ComparePQKey(const Key &key2) const
{
if (StateId::GetStateType(StateId()) != PQ && StateId::GetStateType(key2.StateId()) != PQ) {
return NO_PQ_DATA;
} else if (StateId::GetStateType(StateId()) != PQ) {
return 1;
} else if (StateId::GetStateType(key2.StateId()) != PQ) {
return -1;
}
auto cmp = PQBinaryDataComparator::Compare(mPriKey.KeyData(), mPriKey.KeyLen(), key2.mPriKey.KeyData(),
key2.mPriKey.KeyLen());
cmp = cmp > 0 ? 1 : (cmp < 0 ? -1 : 0);
return cmp;
}
* Is equal to other key.
* @param other other key.
* @return return true if equals, else return false.
*/
inline bool Equal(const Key &other)
{
return Compare(other) == 0;
}
* Has the same primary key node with other key.
* @param other other key.
* @return return true if has the same primary key node, else return false.
*/
inline bool HasSamePriKeyNode(const Key &other)
{
bool hasSamePriKey = (mPriKey.CompareKeyNode(other.mPriKey) == 0);
return hasSamePriKey && this->mPriKey.StateId() == other.mPriKey.StateId();
}
* This key has secondary key or not.
* @return return true if it has secondary key, else return false.
*/
inline bool HasSecKey() const
{
return mHasSecKey;
}
* This key prefix key or not, if it has secondary key and secondary key is nullptr, it is a prefix key.
* @return return true if it is prefix key, else return false.
*/
inline bool IsPrefixKey() const
{
return mHasSecKey && mSecKey.KeyData() == nullptr;
}
* This key is end key or not, it for prefix search,
* @return
*/
inline bool IsEndKey() const
{
return mFlag & END_KEY_FLAG;
}
inline bool IsPqKey() const
{
return StateId::GetStateType(mPriKey.StateId()) == PQ;
}
* Get stateId.
* @return stateId.
*/
inline uint16_t StateId() const
{
return mPriKey.StateId();
}
* Get mixed hash code, mixed hash code is hash code of hole key,
* include stateId, primary key (namespace if it exists) and secondary key info.
* @return mixed hash code.
*/
inline uint32_t MixedHashCode() const
{
return mMixedHashCode;
}
* Get key hash code. key hash code is from flink, PrimaryKeyHashCode is KeyHashCode ^ NamespaceHashCode (if it
* exists), so KeyHashCode = PrimaryKeyHashCode ^ NamespaceHashCode.
* @return key hash code.
*/
inline uint32_t KeyHashCode() const
{
return mPriKey.HashCode() ^ NsHashCode();
}
* Get primary key.
* @return primary key.
*/
inline const PriKeyNode &PriKey() const
{
return mPriKey;
}
* Get secondary key.
* @return secondary key.
*/
inline const SecKeyNode &SecKey() const
{
return mSecKey;
}
* Buffer object to store primary key data and secondary key data, it used for lifecycle management of this key.
* @return buffer object.
*/
inline BufferRef Buffer() const
{
return mBuffer;
}
* Obtains the offset of primary key data in the data.
* @return
*/
inline uint32_t BufferOffsetOfPriKeyData() const
{
return BufferOffset(mPriKey);
}
* Obtains the offset of second key data in the data.
* @return
*/
inline uint32_t BufferOffsetOfSecKeyData() const
{
return BufferOffset(mSecKey);
}
inline void PriKeyData(uint8_t *data)
{
mPriKey.KeyData(data);
}
inline void SecKeyData(uint8_t *data)
{
mSecKey.KeyData(data);
}
* For printing key.
* @return key string.
*/
inline std::string ToString() const
{
std::ostringstream oss;
oss << "MixedHashCode: " << mMixedHashCode << ", PriKey: " << mPriKey.ToString();
if (mHasSecKey) {
oss << ", SecKey: " << mSecKey.ToString();
}
return oss.str();
}
protected:
* Get namespace hashcode.
* @return return namespace hashcode if it exists, else return false.
*/
inline uint32_t NsHashCode() const
{
return mHasNameSpace ? mPriKey.NsHashCode() : 0;
}
private:
* Obtains the offset of key data in the data.
* @param keyNode
* @return
*/
inline uint32_t BufferOffset(const KeyNode &keyNode) const
{
if (mBuffer != nullptr && keyNode.KeyData() != nullptr && mBuffer->Data() != nullptr &&
keyNode.KeyData() >= mBuffer->Data()) {
return keyNode.KeyData() - mBuffer->Data();
}
return 0;
}
protected:
* NOTICE: Align according to the size of the CPU Cache Line, which is 64 bytes. The key cannot exceed 64 bytes!!!
*/
uint8_t mHasSecKey{ false };
uint8_t mHasNameSpace{ false };
uint8_t mFlag{ 0 };
uint8_t mReserved;
uint32_t mMixedHashCode{ 0 };
PriKeyNode mPriKey;
SecKeyNode mSecKey;
BufferRef mBuffer{ nullptr };
};
struct PriKeyNodeHash {
std::size_t operator()(const PriKeyNodePtr &key) const
{
return key->HashCode();
}
};
struct PriKeyNodeEqual {
bool operator()(const PriKeyNodePtr a, const PriKeyNodePtr b) const
{
return a->CompareStateIdFirst(*b) == 0;
}
};
struct SecKeyNodeHash {
std::size_t operator()(const SecKeyNodePtr &key) const
{
return key->HashCode();
}
};
struct SecKeyNodeEqual {
bool operator()(const SecKeyNodePtr a, const SecKeyNodePtr b) const
{
return a->CompareKeyNode(*b) == 0;
}
};
struct KeyHash {
std::size_t operator()(const KeyPtr key) const
{
return key->MixedHashCode();
}
};
struct KeyEqual {
bool operator()(const KeyPtr a, const KeyPtr b) const
{
return a->Compare(*b) == 0;
}
};
using PriKeyFilter = std::function<bool(const PriKeyNode &priKey)>;
using SecKeyFilter = std::function<bool(const SecKeyNode &secKey)>;
using KeyFilter = std::function<bool(const Key &key)>;
}
}
#endif