* 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_VALUE_H
#define BOOST_SS_VALUE_H
#include <iomanip>
#include "binary/byte_buffer.h"
#include "binary/key/key.h"
#include "bss_types.h"
#include "value_type.h"
namespace ock {
namespace bss {
struct Value {
public:
* Initialize value.
* @param valueType value type.
* @param valueLen value length.
* @param valueData value data.
* @param seqId value seq id.
* @param buffer buffer object, store value data, for lifecycle management of this value.
*/
inline void Init(uint8_t valueType, uint32_t valueLen, const uint8_t *valueData, uint64_t seqId,
const BufferRef &buffer)
{
mValueType = valueType;
mValueLen = valueLen;
mValueData = valueData;
mSeqId = seqId;
mBuffer = buffer;
}
inline void Init(uint8_t valueType, uint32_t valueLen, const uint8_t *valueData, uint64_t seqId)
{
Init(valueType, valueLen, valueData, seqId, nullptr);
}
inline void Init(const uint8_t *valueData, uint32_t valueLen, const BufferRef &buffer)
{
Init(NO_0, valueLen, valueData, NO_0, buffer);
}
* Initialize value.
* @param valueType value type.
* @param seqId value seq id.
*/
inline void Init(uint8_t valueType, uint64_t seqId)
{
mValueType = valueType;
mSeqId = seqId;
}
* Initialize value by other value.
* @param other other value.
*/
inline void Init(const Value &other)
{
mValueType = other.mValueType;
mValueLen = other.mValueLen;
mValueData = other.mValueData;
mSeqId = other.mSeqId;
mBuffer = other.mBuffer;
}
inline bool IsNull() const
{
return mBuffer == nullptr || mValueData == nullptr;
}
* Get value type, it define in @ValueType
* @return value type.
*/
inline uint8_t ValueType() const
{
return mValueType;
}
* Get value length.
* @return value length.
*/
inline uint32_t ValueLen() const
{
return mValueLen;
}
* Get value data.
* @return value data, it is store in BufferRef.
*/
inline const uint8_t *ValueData() const
{
return mValueData;
}
* Get value seq id, seq id is for updating value, the newer value has bigger id.
* @return seq id.
*/
inline uint64_t SeqId() const
{
return mSeqId;
}
* Set the seqId
* @param seqId seqId
*/
inline void SetSeqId(uint64_t seqId)
{
mSeqId = seqId;
}
* Buffer object to store value data, it used for lifecycle management of this value.
* @return buffer object.
*/
inline BufferRef Buffer() const
{
return mBuffer;
}
using BufferAllocator = std::function<BufferRef(uint32_t size)>;
* Merge with older value.
* @param olderValue older value.
* @param allocator allocator for allocate merged value buffer.
* @return return BSS_OK if success, else return BSS_ERR.
*/
inline BResult MergeWithOlderValue(const Value &olderValue, BufferAllocator allocator)
{
if (mValueType == APPEND) {
if (olderValue.mValueType == DELETE) {
mValueType = PUT;
return BSS_OK;
} else {
auto mergedValue = MergeValue(*this, olderValue, allocator);
if (mergedValue.second == nullptr) {
LOG_WARN("Failed to merge value." << olderValue.ToString() << ", " << ToString());
return BSS_ALLOC_FAIL;
}
mValueType = olderValue.ValueType();
mValueLen = mergedValue.first;
mValueData = mergedValue.second->Data();
mBuffer = mergedValue.second;
return BSS_OK;
}
}
return BSS_OK;
}
* Merge with newer value.
* @param newerValue newer value.
* @param allocator allocator for allocate merged value buffer.
* @return return BSS_OK if success, else return BSS_ERR.
*/
inline BResult MergeWithNewerValue(const Key &key, const Value &newerValue, BufferAllocator allocator,
std::function<void(const Key &, const Value &)> action)
{
mSeqId = newerValue.mSeqId;
if (newerValue.mValueType == APPEND) {
if (mValueType == DELETE) {
mValueType = PUT;
mValueData = newerValue.mValueData;
mValueLen = newerValue.mValueLen;
mBuffer = newerValue.mBuffer;
return BSS_OK;
} else {
auto mergedValue = MergeValue(newerValue, *this, allocator);
if (mergedValue.second == nullptr) {
LOG_WARN("Failed to merge value." << ToString() << ", " << newerValue.ToString());
return BSS_ALLOC_FAIL;
}
mValueLen = mergedValue.first;
mValueData = mergedValue.second->Data();
mBuffer = mergedValue.second;
return BSS_OK;
}
}
action(key, *this);
mValueType = newerValue.mValueType;
mValueData = newerValue.mValueData;
mValueLen = newerValue.mValueLen;
mBuffer = newerValue.mBuffer;
return BSS_OK;
}
* For printing value.
* @return value string.
*/
inline std::string ToString() const
{
std::ostringstream oss;
oss << "ValueType: " << static_cast<int>(mValueType) << ", ValueLen: " << mValueLen << ", seqId: " << mSeqId
<< ", ValueData: [";
if (UNLIKELY(mValueData != nullptr)) {
uint32_t printLen = std::min(NO_10, mValueLen);
for (uint32_t i = 0; i < printLen; i++) {
oss << std::hex << std::uppercase << std::setw(NO_2) << std::setfill('0')
<< static_cast<int>(mValueData[i]);
}
} else {
oss << "null";
}
oss << "]";
return oss.str();
}
protected:
uint64_t mSeqId{ INVALID_U64 };
uint8_t mValueType{ INVALID_U8 };
uint8_t mReserved[3]{};
uint32_t mValueLen{ 0 };
const uint8_t *mValueData{ nullptr };
BufferRef mBuffer{ nullptr };
private:
* Merge two value together, older value is before newer value in merged value.
* @param newerValue newer value.
* @param olderValue older value.
* @param allocator allocator for allocate merged value buffer.
* @return merged value length and buffer object.
*/
inline static std::pair<uint32_t, BufferRef> MergeValue(const Value &newerValue, const Value &olderValue,
BufferAllocator allocator)
{
uint32_t olderValueLen = olderValue.ValueLen();
uint32_t newerValueLen = newerValue.ValueLen();
if (UNLIKELY(UINT32_MAX - olderValueLen < newerValueLen)) {
LOG_ERROR("Merge value too long:" << olderValueLen << "," << newerValueLen);
return { 0, nullptr };
}
uint32_t mergedValueLen = olderValueLen + newerValueLen;
BufferRef buffer = allocator(mergedValueLen);
if (UNLIKELY(buffer == nullptr)) {
LOG_WARN("Failed to allocate buffer. length:" << mergedValueLen);
return { 0, nullptr };
}
errno_t ret1 = memcpy_s(buffer->Data(), mergedValueLen, olderValue.ValueData(), olderValueLen);
if (UNLIKELY(ret1 != EOK)) {
LOG_ERROR("memcpy_s failed for older value, ret = " << ret1);
return { 0, nullptr };
}
errno_t ret2 = memcpy_s(buffer->Data() + olderValueLen, mergedValueLen - olderValueLen, newerValue.ValueData(),
newerValueLen);
if (UNLIKELY(ret2 != EOK)) {
LOG_ERROR("memcpy_s failed for newer value, ret = " << ret2);
return { 0, nullptr };
}
return { mergedValueLen, buffer };
}
};
using ValuePtr = Value *;
using ValueRef = std::shared_ptr<Value>;
using BlobValueTransformFunc = std::function<BResult(uint64_t, uint32_t, uint64_t, Value &)>;
}
}
#endif