#include "scheme.h" #include "scimpl_private.h" #include #include namespace NSc { TStringBufs& TValue::DictKeys(TStringBufs& vs, bool sorted) const { if (!IsDict()) { return vs; } const ::NSc::TDict& dict = GetDict(); vs.reserve(vs.size() + dict.size()); for (const auto& it : dict) vs.push_back(it.first); if (sorted) { Sort(vs.begin(), vs.end()); } return vs; } TStringBufs TValue::DictKeys(bool sorted) const { TStringBufs bufs; DictKeys(bufs, sorted); return bufs; } TValue& TValue::MergeUpdate(const TValue& delta, TMaybe mergeOptions) { return DoMerge(delta, false, mergeOptions); } TValue& TValue::ReverseMerge(const TValue& delta, TMaybe mergeOptions) { return DoMerge(delta, true, mergeOptions); } TValue& TValue::MergeUpdateJson(TStringBuf data, TMaybe mergeOptions) { return MergeUpdate(FromJson(data), mergeOptions); } TValue& TValue::ReverseMergeJson(TStringBuf data, TMaybe mergeOptions) { return ReverseMerge(FromJson(data), mergeOptions); } bool TValue::MergeUpdateJson(TValue& v, TStringBuf data, TMaybe mergeOptions) { NSc::TValue m; if (!FromJson(m, data)) { return false; } v.MergeUpdate(m, mergeOptions); return true; } bool TValue::ReverseMergeJson(TValue& v, TStringBuf data, TMaybe mergeOptions) { NSc::TValue m; if (!FromJson(m, data)) { return false; } v.ReverseMerge(m, mergeOptions); return true; } TValue TValue::Clone() const { return TValue().CopyFrom(*this); } TValue TValue::CreateNew() const { return Y_LIKELY(TheCore) ? TValue(TheCore->Pool) : TValue(); } double TValue::ForceNumber(double deflt) const { const TScCore& core = Core(); if (core.IsNumber()) { return core.GetNumber(deflt); } if (TStringBuf str = core.GetString(TStringBuf())) { { double result = 0; if (TryFromString(str, result)) { return result; } } { i64 result = 0; if (TryFromString(str, result)) { return result; } } { ui64 result = 0; if (TryFromString(str, result)) { return result; } } } return deflt; } i64 TValue::ForceIntNumber(i64 deflt) const { const TScCore& core = Core(); if (core.IsNumber()) { return core.GetIntNumber(deflt); } if (TStringBuf str = core.GetString(TStringBuf())) { { i64 result = 0; if (TryFromString(str, result)) { return result; } } { ui64 result = 0; if (TryFromString(str, result)) { return result; } } { double result = 0; if (TryFromString(str, result)) { return result; } } } return deflt; } TString TValue::ForceString(const TString& deflt) const { const TScCore& core = Core(); if (core.IsString()) { return ToString(core.GetString(TStringBuf())); } if (core.IsIntNumber()) { return ToString(core.GetIntNumber(0)); } if (core.IsNumber()) { return ToString(core.GetNumber(0)); } return deflt; } TValue& /*this*/ TValue::CopyFrom(const TValue& other) { if (Same(*this, other)) { return *this; } using namespace NImpl; return DoCopyFromImpl(other, GetTlsInstance(), GetTlsInstance()); } TValue& TValue::DoCopyFromImpl(const TValue& other, NImpl::TSelfLoopContext& otherLoopCtx, NImpl::TSelfOverrideContext& selfOverrideCtx) { if (Same(*this, other)) { return *this; } CoreMutableForSet(); // trigger COW TScCore& selfCore = *TheCore; const TScCore& otherCore = other.Core(); NImpl::TSelfLoopContext::TGuard loopCheck(otherLoopCtx, otherCore); NImpl::TSelfOverrideContext::TGuard overrideGuard(selfOverrideCtx, selfCore); selfCore.SetNull(); if (!loopCheck.Ok) { return *this; // a loop encountered (and asserted), skip the back reference } switch (otherCore.ValueType) { default: Y_ASSERT(false); [[fallthrough]]; case EType::Null: break; case EType::Bool: selfCore.SetBool(otherCore.IntNumber); break; case EType::IntNumber: selfCore.SetIntNumber(otherCore.IntNumber); break; case EType::FloatNumber: selfCore.SetNumber(otherCore.FloatNumber); break; case EType::String: if (selfCore.Pool.Get() == otherCore.Pool.Get()) { selfCore.SetOwnedString(otherCore.String); } else { selfCore.SetString(otherCore.String); } break; case EType::Array: selfCore.SetArray(); for (const TValue& e : otherCore.GetArray()) { selfCore.Push().DoCopyFromImpl(e, otherLoopCtx, selfOverrideCtx); } break; case EType::Dict: { TCorePtr tmp = NewCore(selfCore.Pool); auto& tmpCore = *tmp; tmpCore.SetDict(); const TDict& d = otherCore.GetDict(); tmpCore.Dict.reserve(d.size()); for (const TDict::value_type& e : d) { tmpCore.Add(e.first).DoCopyFromImpl(e.second, otherLoopCtx, selfOverrideCtx); } TheCore = std::move(tmp); break; } } return *this; } TValue& TValue::Swap(TValue& v) { DoSwap(TheCore, v.TheCore); DoSwap(CopyOnWrite, v.CopyOnWrite); return *this; } bool TValue::Same(const TValue& a, const TValue& b) { return a.TheCore.Get() == b.TheCore.Get(); } bool TValue::SamePool(const TValue& a, const TValue& b) { return Same(a, b) || (a.TheCore && b.TheCore && a.TheCore->Pool.Get() == b.TheCore->Pool.Get()); } bool TValue::Equal(const TValue& a, const TValue& b) { if (Same(a, b)) { return true; } const NSc::TValue::TScCore& coreA = a.Core(); const NSc::TValue::TScCore& coreB = b.Core(); if (coreA.IsNumber() && coreB.IsNumber()) { return coreA.GetIntNumber(0) == coreB.GetIntNumber(0) && coreA.GetNumber(0) == coreB.GetNumber(0); } if (coreA.ValueType != coreB.ValueType) { return false; } if (coreA.IsString()) { std::string_view strA = coreA.String; std::string_view strB = coreB.String; if (strA != strB) { return false; } } else if (coreA.IsArray()) { const TArray& arrA = coreA.Array; const TArray& arrB = coreB.Array; if (arrA.size() != arrB.size()) { return false; } for (size_t i = 0; i < arrA.size(); ++i) { if (!Equal(arrA[i], arrB[i])) { return false; } } } else if (coreA.IsDict()) { const ::NSc::TDict& dictA = coreA.Dict; const ::NSc::TDict& dictB = coreB.Dict; if (dictA.size() != dictB.size()) { return false; } for (const auto& ita : dictA) { ::NSc::TDict::const_iterator itb = dictB.find(ita.first); if (itb == dictB.end() || !Equal(ita.second, itb->second)) { return false; } } } return true; } TValue& TValue::DoMerge(const TValue& delta, bool lowPriorityDelta, TMaybe mergeOptions) { if (Same(*this, delta)) { return *this; } using namespace NImpl; return DoMergeImpl(delta, lowPriorityDelta, mergeOptions, GetTlsInstance(), GetTlsInstance()); } TValue& TValue::DoMergeImpl(const TValue& delta, bool lowPriorityDelta, TMaybe mergeOptions, NImpl::TSelfLoopContext& otherLoopCtx, NImpl::TSelfOverrideContext& selfOverrideGuard) { if (Same(*this, delta)) { return *this; } bool allowMergeArray = mergeOptions.Defined() && mergeOptions->ArrayMergeMode == TMergeOptions::EArrayMergeMode::Merge; if (delta.IsDict() && (!lowPriorityDelta || IsDict() || IsNull())) { TScCore& core = CoreMutable(); const TScCore& deltaCore = delta.Core(); NImpl::TSelfLoopContext::TGuard loopCheck(otherLoopCtx, deltaCore); if (!loopCheck.Ok) { return *this; // a loop encountered (and asserted), skip the back reference } if (!lowPriorityDelta || IsNull()) { SetDict(); } const TDict& ddelta = deltaCore.Dict; for (const auto& dit : ddelta) { core.GetOrAdd(dit.first).DoMergeImpl(dit.second, lowPriorityDelta, mergeOptions, otherLoopCtx, selfOverrideGuard); } } else if (delta.IsArray() && allowMergeArray && (!lowPriorityDelta || IsArray() || IsNull())) { TScCore& core = CoreMutable(); const TScCore& deltaCore = delta.Core(); NImpl::TSelfLoopContext::TGuard loopCheck(otherLoopCtx, deltaCore); if (!loopCheck.Ok) { return *this; // a loop encountered (and asserted), skip the back reference } if (!lowPriorityDelta || IsNull()) { SetArray(); } Y_ASSERT(IsArray()); const TArray& adelta = deltaCore.Array; if (adelta.size() > core.Array.size()) { core.Array.resize(adelta.size()); } for (size_t i = 0; i < adelta.size(); ++i) { core.Array[i].DoMergeImpl(adelta[i], lowPriorityDelta, mergeOptions, otherLoopCtx, selfOverrideGuard); } } else if (!delta.IsNull() && (!lowPriorityDelta || IsNull())) { DoCopyFromImpl(delta, otherLoopCtx, selfOverrideGuard); } return *this; } NJson::TJsonValue TValue::ToJsonValue() const { using namespace NImpl; return ToJsonValueImpl(GetTlsInstance()); } NJson::TJsonValue TValue::ToJsonValueImpl(NImpl::TSelfLoopContext& loopCtx) const { const TScCore& core = Core(); switch (core.ValueType) { default: case EType::Null: return NJson::TJsonValue(NJson::JSON_NULL); case EType::Bool: return NJson::TJsonValue(core.GetBool()); case EType::IntNumber: return NJson::TJsonValue(core.GetIntNumber()); case EType::FloatNumber: return NJson::TJsonValue(core.GetNumber()); case EType::String: return NJson::TJsonValue(core.String); case EType::Array: { NImpl::TSelfLoopContext::TGuard loopGuard(loopCtx, core); if (!loopGuard.Ok) { return NJson::TJsonValue(NJson::JSON_NULL); } NJson::TJsonValue result(NJson::JSON_ARRAY); const TArray& arr = core.Array; for (const auto& item : arr) { result.AppendValue(NJson::TJsonValue::UNDEFINED) = item.ToJsonValueImpl(loopCtx); } return result; } case EType::Dict: { NImpl::TSelfLoopContext::TGuard loopGuard(loopCtx, core); if (!loopGuard.Ok) { return NJson::TJsonValue(NJson::JSON_NULL); } NJson::TJsonValue result(NJson::JSON_MAP); const TDict& dict = core.Dict; for (const auto& item : dict) { result.InsertValue(item.first, NJson::TJsonValue::UNDEFINED) = item.second.ToJsonValueImpl(loopCtx); } return result; } } } TValue TValue::FromJsonValue(const NJson::TJsonValue& val) { TValue result; FromJsonValue(result, val); return result; } TValue& TValue::FromJsonValue(TValue& res, const NJson::TJsonValue& val) { TScCore& core = res.CoreMutableForSet(); core.SetNull(); switch (val.GetType()) { default: case NJson::JSON_UNDEFINED: case NJson::JSON_NULL: break; case NJson::JSON_BOOLEAN: core.SetBool(val.GetBoolean()); break; case NJson::JSON_INTEGER: core.SetIntNumber(val.GetInteger()); break; case NJson::JSON_UINTEGER: core.SetIntNumber(val.GetUInteger()); break; case NJson::JSON_DOUBLE: core.SetNumber(val.GetDouble()); break; case NJson::JSON_STRING: core.SetString(val.GetString()); break; case NJson::JSON_ARRAY: { core.SetArray(); for (const auto& item : val.GetArray()) { FromJsonValue(core.Push(), item); } break; } case NJson::JSON_MAP: { core.SetDict(); for (const auto& item : val.GetMap()) { FromJsonValue(core.Add(item.first), item.second); } break; } } return res; } struct TDefaults { TValue::TPoolPtr Pool = MakeIntrusive(); TValue::TScCore Core{Pool}; }; const TValue::TScCore& TValue::DefaultCore() { return Default().Core; } const TArray& TValue::DefaultArray() { return Default().Core.Array; } const TDict& TValue::DefaultDict() { return Default().Core.Dict; } const TValue& TValue::DefaultValue() { return *FastTlsSingleton(); } bool TValue::IsSameOrAncestorOf(const TValue& other) const { using namespace NImpl; return IsSameOrAncestorOfImpl(other.Core(), GetTlsInstance()); } bool TValue::IsSameOrAncestorOfImpl(const TScCore& other, NImpl::TSelfLoopContext& loopCtx) const { const TScCore& core = Core(); if (&core == &other) { return true; } switch (core.ValueType) { default: return false; case EType::Array: { NImpl::TSelfLoopContext::TGuard loopGuard(loopCtx, core); if (!loopGuard.Ok) { return false; } for (const auto& item : core.Array) { if (item.IsSameOrAncestorOfImpl(other, loopCtx)) { return true; } } return false; } case EType::Dict: { NImpl::TSelfLoopContext::TGuard loopGuard(loopCtx, core); if (!loopGuard.Ok) { return false; } for (const auto& item : core.Dict) { if (item.second.IsSameOrAncestorOfImpl(other, loopCtx)) { return true; } } return false; } } } namespace NPrivate { int CompareStr(const NSc::TValue& a, TStringBuf b) { return a.GetString().compare(b); } int CompareInt(const NSc::TValue& a, i64 r) { i64 l = a.GetIntNumber(); return l < r ? -1 : l > r ? 1 : 0; } int CompareFloat(const NSc::TValue& a, double r) { double l = a.GetNumber(); return l < r ? -1 : l > r ? 1 : 0; } } bool operator==(const NSc::TValue& a, const NSc::TValue& b) { return NSc::TValue::Equal(a, b); } bool operator!=(const NSc::TValue& a, const NSc::TValue& b) { return !NSc::TValue::Equal(a, b); } #define LIBRARY_SCHEME_DECLARE_TVALUE_OPS_IMPL(T, Impl) \ bool operator==(const NSc::TValue& a, T b) { \ return NPrivate::Impl(a, b) == 0; \ } \ bool operator==(T b, const NSc::TValue& a) { \ return NPrivate::Impl(a, b) == 0; \ } \ bool operator!=(const NSc::TValue& a, T b) { \ return NPrivate::Impl(a, b) != 0; \ } \ bool operator!=(T b, const NSc::TValue& a) { \ return NPrivate::Impl(a, b) != 0; \ } \ bool operator<=(const NSc::TValue& a, T b) { \ return NPrivate::Impl(a, b) <= 0; \ } \ bool operator<=(T b, const NSc::TValue& a) { \ return NPrivate::Impl(a, b) >= 0; \ } \ bool operator>=(const NSc::TValue& a, T b) { \ return NPrivate::Impl(a, b) >= 0; \ } \ bool operator>=(T b, const NSc::TValue& a) { \ return NPrivate::Impl(a, b) <= 0; \ } \ bool operator<(const NSc::TValue& a, T b) { \ return NPrivate::Impl(a, b) < 0; \ } \ bool operator<(T b, const NSc::TValue& a) { \ return NPrivate::Impl(a, b) > 0; \ } \ bool operator>(const NSc::TValue& a, T b) { \ return NPrivate::Impl(a, b) > 0; \ } \ bool operator>(T b, const NSc::TValue& a) { \ return NPrivate::Impl(a, b) < 0; \ } #define LIBRARY_SCHEME_DECLARE_TVALUE_INT_OPS_IMPL(T) \ LIBRARY_SCHEME_DECLARE_TVALUE_OPS_IMPL(signed T, CompareInt) \ LIBRARY_SCHEME_DECLARE_TVALUE_OPS_IMPL(unsigned T, CompareInt) //LIBRARY_SCHEME_DECLARE_TVALUE_OPS_IMPL(bool, CompareInt) LIBRARY_SCHEME_DECLARE_TVALUE_OPS_IMPL(char, CompareInt) LIBRARY_SCHEME_DECLARE_TVALUE_INT_OPS_IMPL(char) LIBRARY_SCHEME_DECLARE_TVALUE_INT_OPS_IMPL(short) LIBRARY_SCHEME_DECLARE_TVALUE_INT_OPS_IMPL(int) LIBRARY_SCHEME_DECLARE_TVALUE_INT_OPS_IMPL(long) LIBRARY_SCHEME_DECLARE_TVALUE_INT_OPS_IMPL(long long) LIBRARY_SCHEME_DECLARE_TVALUE_OPS_IMPL(float, CompareFloat) LIBRARY_SCHEME_DECLARE_TVALUE_OPS_IMPL(double, CompareFloat) LIBRARY_SCHEME_DECLARE_TVALUE_OPS_IMPL(TStringBuf, CompareStr) LIBRARY_SCHEME_DECLARE_TVALUE_OPS_IMPL(const TString&, CompareStr) LIBRARY_SCHEME_DECLARE_TVALUE_OPS_IMPL(const char* const, CompareStr) #undef LIBRARY_SCHEME_DECLARE_TVALUE_OPS_IMPL #undef LIBRARY_SCHEME_DECLARE_TVALUE_INT_OPS_IMPL } template <> void Out(IOutputStream& o, TTypeTraits::TFuncParam v) { o.Write(v.ToJson(true)); }