#pragma once
#include <library/cpp/deprecated/atomic/atomic.h>
#include <util/system/types.h>
#include <util/system/compiler.h>
#include <util/generic/array_ref.h>
namespace NActors {
class TSharedData {
public:
class IOwner {
public:
virtual ~IOwner() = default;
virtual void Deallocate(char*) noexcept = 0;
};
struct TPrivateHeader {
size_t AllocSize;
size_t Pad;
};
static_assert(sizeof(TPrivateHeader) == 16, "TPrivateHeader has an unexpected size");
struct THeader {
TAtomic RefCount;
IOwner* Owner;
};
static_assert(sizeof(THeader) == 16, "THeader has an unexpected size");
enum : size_t {
PrivateHeaderSize = sizeof(TPrivateHeader),
HeaderSize = sizeof(THeader),
OverheadSize = PrivateHeaderSize + HeaderSize,
MaxDataSize = (std::numeric_limits<size_t>::max() - OverheadSize)
};
public:
TSharedData() noexcept
: Data_(nullptr)
, Size_(0)
{ }
~TSharedData() noexcept {
Release();
}
TSharedData(const TSharedData& other) noexcept
: Data_(other.Data_)
, Size_(other.Size_)
{
AddRef();
}
TSharedData(TSharedData&& other) noexcept
: Data_(other.Data_)
, Size_(other.Size_)
{
other.Data_ = nullptr;
other.Size_ = 0;
}
TSharedData& operator=(const TSharedData& other) noexcept {
if (this != &other) {
Release();
Data_ = other.Data_;
Size_ = other.Size_;
AddRef();
}
return *this;
}
TSharedData& operator=(TSharedData&& other) noexcept {
if (this != &other) {
Release();
Data_ = other.Data_;
Size_ = other.Size_;
other.Data_ = nullptr;
other.Size_ = 0;
}
return *this;
}
Y_FORCE_INLINE explicit operator bool() const { return Size_ > 0; }
Y_FORCE_INLINE char* mutable_data() { Y_VERIFY_DEBUG(IsPrivate()); return Data_; }
Y_FORCE_INLINE char* mutable_begin() { Y_VERIFY_DEBUG(IsPrivate()); return Data_; }
Y_FORCE_INLINE char* mutable_end() { Y_VERIFY_DEBUG(IsPrivate()); return Data_ + Size_; }
Y_FORCE_INLINE const char* data() const { return Data_; }
Y_FORCE_INLINE const char* begin() const { return Data_; }
Y_FORCE_INLINE const char* end() const { return Data_ + Size_; }
Y_FORCE_INLINE size_t size() const { return Size_; }
/**
* Trims data to the specified size
* Underlying data is not reallocated
* Returns trimmed amount in bytes
*/
size_t TrimBack(size_t size) noexcept {
size_t trimmed = 0;
if (Size_ > size) {
trimmed = Size_ - size;
if (!size) {
Release();
Data_ = nullptr;
}
Size_ = size;
}
return trimmed;
}
/**
* Copies data to new allocated buffer if data is shared
* New container loses original owner
* Returns pointer to mutable buffer
*/
char* Detach() {
if (IsShared()) {
*this = TSharedData::Copy(data(), size());
}
return Data_;
}
/**
* Returns a view of underlying data starting with pos and up to len bytes
*/
TStringBuf Slice(size_t pos = 0, size_t len = -1) const noexcept {
pos = Min(pos, Size_);
len = Min(len, Size_ - pos);
return { Data_ + pos, len };
}
explicit operator TStringBuf() const noexcept {
return Slice();
}
bool IsPrivate() const {
return Data_ ? IsPrivate(Header()) : true;
}
bool IsShared() const {
return !IsPrivate();
}
TString ToString() const {
return TString(data(), size());
}
/**
* Attach to pre-allocated data with a preceding THeader
*/
static TSharedData AttachUnsafe(char* data, size_t size) noexcept {
TSharedData result;
result.Data_ = data;
result.Size_ = size;
return result;
}
/**
* Make uninitialized buffer of the specified size
*/
static TSharedData Uninitialized(size_t size) {
return AttachUnsafe(Allocate(size), size);
}
/**
* Make a copy of the specified data
*/
static TSharedData Copy(const void* data, size_t size) {
TSharedData result = Uninitialized(size);
if (size) {
::memcpy(result.Data_, data, size);
}
return result;
}
/**
* Make a copy of the specified data
*/
static TSharedData Copy(TArrayRef<const char> data) {
return Copy(data.data(), data.size());
}
private:
Y_FORCE_INLINE THeader* Header() const noexcept {
Y_VERIFY_DEBUG(Data_);
return reinterpret_cast<THeader*>(Data_ - sizeof(THeader));
}
static bool IsPrivate(THeader* header) noexcept {
return 1 == AtomicGet(header->RefCount);
}
void AddRef() noexcept {
if (Data_) {
AtomicIncrement(Header()->RefCount);
}
}
void Release() noexcept {
if (Data_) {
auto* header = Header();
if (IsPrivate(header) || 0 == AtomicDecrement(header->RefCount)) {
if (auto* owner = header->Owner) {
owner->Deallocate(Data_);
} else {
Deallocate(Data_);
}
}
}
}
private:
static char* Allocate(size_t size);
static void Deallocate(char* data) noexcept;
private:
char* Data_;
size_t Size_;
};
}