#pragma once #include "scimpl_defs.h" #include "fwd.h" #include #include #include namespace NSc { #ifdef _MSC_VER #pragma warning(disable : 4521 4522) #endif struct TMergeOptions { enum class EArrayMergeMode { Replace, Merge }; EArrayMergeMode ArrayMergeMode = EArrayMergeMode::Replace; }; // todo: try to remove some rarely used methods class TValue { public: enum class EType { Null = 0 /* "Null" */, Bool /* "Bool" */, IntNumber /* "Int" */, FloatNumber /* "Float" */, String /* "String" */, Array /* "Array" */, Dict /* "Dict" */ }; struct TScCore; using TCorePtr = TIntrusivePtr; using TPoolPtr = TIntrusivePtr; using TArray = ::NSc::TArray; using TDict = ::NSc::TDict; private: // A TValue instance has only these 3 fields mutable TCorePtr TheCore; // a pointer to a refcounted (kind of) variant bool CopyOnWrite = false; // a flag that thevalue is a COW shallow copy and should produce a deep copy once modified // Thus all copies of a TValue are by default shallow. Use TValue::Clone to force a deep copy. // A COW copy will see changes in its parent, but no change in the COW copy will propagate to its parent. public: // XXX: A newly constructed standalone TValue instance (even null!) consumes ~4 KB of memory because it allocates a memory pool inside. // Consider building a tree of TValues in top-down order (from root to leaves) to share a single pool // instead of creating child TValues first and appending them to a parent TValue. // All somehow cached values should be constant, otherwise the shared pool can grow infinitely. inline TValue(); inline TValue(TValue& v); inline TValue(const TValue& v); inline TValue(TValue&& v) noexcept; public: // Operators inline TValue(double t); inline TValue(unsigned long long t); inline TValue(unsigned long t); inline TValue(unsigned t); inline TValue(long long t); inline TValue(long t); inline TValue(int t); // inline TValue(bool b); inline TValue(TStringBuf t); inline TValue(const char*); inline operator double() const; inline operator float() const; inline operator long long() const; inline operator long() const; inline operator int() const; inline operator short() const; inline operator char() const; inline operator unsigned long long() const; inline operator unsigned long() const; inline operator unsigned() const; inline operator unsigned short() const; inline operator unsigned char() const; inline operator signed char() const; inline operator TStringBuf() const; inline operator const ::NSc::TArray&() const; inline operator const ::NSc::TDict&() const; inline TValue& operator=(double t); inline TValue& operator=(unsigned long long t); inline TValue& operator=(unsigned long t); inline TValue& operator=(unsigned t); inline TValue& operator=(long long t); inline TValue& operator=(long t); inline TValue& operator=(int t); // inline TValue& operator=(bool t); inline TValue& operator=(TStringBuf t); inline TValue& operator=(const char* t); inline TValue& operator=(TValue& v) &; inline TValue& operator=(const TValue& v) &; inline TValue& operator=(TValue&& v) & noexcept; inline TValue& operator=(TValue& v) && = delete; inline TValue& operator=(const TValue& v) && = delete; inline TValue& operator=(TValue&& v) && = delete; public: template // ui16 or TStringBuf inline TValue& operator[](const T& idx) { return GetOrAdd(idx); } template // ui16 or TStringBuf inline const TValue& operator[](const T& idx) const { return Get(idx); } public: // Data methods /////////////////////////////////////////////////////////// inline EType GetType() const; inline bool IsNull() const; inline TValue& SetNull(); // returns self, will set type to Null TValue& Clear() { return ClearArray().ClearDict().SetNull(); } public: // Number methods ///////////////////////////////////////////////////////// // Bool, IntNumber and FloatNumber are all compatible. // If a TValue node has one of the types it may as well be used as another. // FloatNumber methods. Forces FloatNumber representation. Compatible with IntNumber and Bool inline bool IsNumber() const; // true if any of FloatNumber, IntNumber, Bool inline double GetNumber(double defaultval = 0) const; // Compatible with Bool, IntNumber and FloatNumber types inline double& GetNumberMutable(double defaultval = 0); // Will switch the type to FloatNumber inline TValue& SetNumber(double val = 0); // returns self, will switch the type to FloatNumber double ForceNumber(double deflt = 0) const; // Best-effort cast to double (will do TryFromString if applicable) // IntNumber methods. Forces integer representation. Compatible with FloatNumber and Bool types. // Note: if you don't care about distinguishing bools, ints and doubles, use *Number methods above inline bool IsIntNumber() const; // true only if IntNumber or Bool inline i64 GetIntNumber(i64 defaultval = 0) const; // Compatible with Bool, IntNumber and FloatNumber types inline i64& GetIntNumberMutable(i64 defaultval = 0); // Will switch the type to IntNumber inline TValue& SetIntNumber(i64 val = 0); // returns self, will switch the type to IntNumber i64 ForceIntNumber(i64 deflt = 0) const; // Best-effort cast to i64 (will do TryFromString for String) // Bool methods. Forces bool representation. Compatible with Float Number and Int Number methods above. // Note: if you don't care about distinguishing Bool, IntNumber and FloatNumber, use *Number methods above inline bool IsBool() const; // true only if Bool inline bool GetBool(bool defaultval = false) const; // Compatible with Bool, IntNumber and FloatNumber types inline TValue& SetBool(bool val = false); // returns self, will switch the type to Bool public: // Arcadia-specific boolean representation support // Tests for explicit True, also checks for arcadia-specific boolean representation bool IsTrue() const { return IsNumber() ? GetNumber() : ::IsTrue(GetString()); } // Tests for explicit False, also checks for arcadia-specific boolean representation bool IsExplicitFalse() const { return IsNumber() ? !GetNumber() : IsFalse(GetString()); } public: // String methods ///////////////////////////////////////////////////////// inline bool IsString() const; inline TStringBuf GetString(TStringBuf defaultval = TStringBuf()) const; inline TValue& SetString(TStringBuf val = TStringBuf()); // returns self TString ForceString(const TString& deflt = TString()) const; // Best-effort cast to TString (will do ToString for numeric types) // todo: remove inline bool StringEmpty() const; inline size_t StringSize() const; public: // Array methods ////////////////////////////////////////////////////////// inline bool IsArray() const; inline const TArray& GetArray() const; inline TArray& GetArrayMutable(); inline TValue& SetArray(); // turns into array if needed, returns self inline TValue& ClearArray(); inline bool Has(size_t idx) const; inline const TValue& Get(size_t idx) const; // returns child or default inline TValue* GetNoAdd(size_t idx); // returns link to existing child or nullptr inline TValue& Push(); // returns new child template TValue& Push(T&& t) { return Push() = std::forward(t); } // returns new child TValue& Insert(ui16 idx) { return InsertUnsafe(idx); } // creates missing values, returns new child template TValue& Insert(ui16 idx, T&& v) { return InsertUnsafe(idx, std::forward(v)); } // creates missing values, returns new child template inline TValue& AppendAll(TIt begin, TIt end); // Append(vec.begin(), vec.end()) template inline TValue& AppendAll(TColl&& coll); // Append(vec) inline TValue& AppendAll(std::initializer_list coll); TValue& GetOrAdd(ui16 idx) { return GetOrAddUnsafe(idx); } // creates missing values, returns new child inline TValue& InsertUnsafe(size_t idx); // creates missing values, returns new child template TValue& InsertUnsafe(size_t idx, T&& t) { return InsertUnsafe(idx) = std::forward(t); } // creates missing values, returns new child inline TValue& GetOrAddUnsafe(size_t idx); // creates missing values, returns new child inline TValue Pop(); // returns popped value inline TValue Delete(size_t idx); // returns deleted value if it existed, NSc::Null() otherwise inline TValue& Front() { return GetOrAdd(0); } // creates missing value, returns child inline const TValue& Front() const { return Get(0); } // returns child or default inline TValue& Back(); // creates missing value, returns child inline const TValue& Back() const; // returns child or default // todo: remove inline bool ArrayEmpty() const; inline size_t ArraySize() const; public: // Dict methods inline bool IsDict() const; inline const TDict& GetDict() const; inline TDict& GetDictMutable(); inline TValue& SetDict(); // turns into dict if not one, returns self inline TValue& ClearDict(); inline bool Has(TStringBuf idx) const; inline const TValue& Get(TStringBuf idx) const; inline TValue* GetNoAdd(TStringBuf idx); // returns link to existing child or nullptr TValue& Add(TStringBuf idx) { return GetOrAdd(idx); } template TValue& Add(TStringBuf idx, T&& t) { return Add(idx) = std::forward(t); } inline TValue& GetOrAdd(TStringBuf idx); // creates missing value, returns child inline TValue Delete(TStringBuf idx); // returns deleted value inline TValue& AddAll(std::initializer_list> t); TStringBufs DictKeys(bool sorted = true) const; TStringBufs& DictKeys(TStringBufs&, bool sorted = true) const; // todo: remove inline bool DictEmpty() const; inline size_t DictSize() const; public: // Json methods //////////////////////////////////////////////// using TJsonOpts = NSc::TJsonOpts; using EJsonOpts = TJsonOpts::EJsonOpts; static const EJsonOpts JO_DEFAULT = TJsonOpts::JO_DEFAULT; static const EJsonOpts JO_SORT_KEYS = TJsonOpts::JO_SORT_KEYS; static const EJsonOpts JO_SKIP_UNSAFE = TJsonOpts::JO_SKIP_UNSAFE; // skip non-utf8 strings static const EJsonOpts JO_PRETTY = TJsonOpts::JO_PRETTY; static const EJsonOpts JO_SAFE = TJsonOpts::JO_SAFE; // JO_SORT_KEYS | JO_SKIP_UNSAFE static const EJsonOpts JO_PARSER_STRICT_WITH_COMMENTS = TJsonOpts::JO_PARSER_STRICT_WITH_COMMENTS; // strict json + strict utf8 static const EJsonOpts JO_PARSER_STRICT = TJsonOpts::JO_PARSER_STRICT; // strict json + strict utf8 + comments are disallowed static const EJsonOpts JO_PARSER_DISALLOW_DUPLICATE_KEYS = TJsonOpts::JO_PARSER_DISALLOW_DUPLICATE_KEYS; [[nodiscard]] static TValue FromJson(TStringBuf, const TJsonOpts& = TJsonOpts()); [[nodiscard]] static TValue FromJsonThrow(TStringBuf, const TJsonOpts& = TJsonOpts()); static bool FromJson(TValue&, TStringBuf, const TJsonOpts& = TJsonOpts()); // TODO: Переименовать ToJson в ToJsonUnsafe, а ToJsonSafe в ToJson TString ToJson(const TJsonOpts& = TJsonOpts()) const; const TValue& ToJson(IOutputStream&, const TJsonOpts& = TJsonOpts()) const; // returns self // ToJson(JO_SORT_KEYS | JO_SKIP_UNSAFE) TString ToJsonSafe(const TJsonOpts& = TJsonOpts()) const; const TValue& ToJsonSafe(IOutputStream&, const TJsonOpts& = TJsonOpts()) const; // ToJson(JO_SORT_KEYS | JO_PRETTY | JO_SKIP_UNSAFE) TString ToJsonPretty(const TJsonOpts& = TJsonOpts()) const; const TValue& ToJsonPretty(IOutputStream&, const TJsonOpts& = TJsonOpts()) const; NJson::TJsonValue ToJsonValue() const; static TValue FromJsonValue(const NJson::TJsonValue&); static TValue& FromJsonValue(TValue&, const NJson::TJsonValue&); // returns self static TJsonOpts MakeOptsSafeForSerializer(TJsonOpts = TJsonOpts()); static TJsonOpts MakeOptsPrettyForSerializer(TJsonOpts = TJsonOpts()); public: // Merge methods //////////////////////////////////////////////// /* * LHS.MergeUpdate(RHS): * 1. Dict <- Dict: * - Copy all nonconflicting key-value pairs from RHS to LHS. * - For every pair of conflicting values apply LHS[key].MergeUpdate(RHS[key]). * 2. Anything <- Null: * - Do nothing. * 3. Other conflicts: * - Copy RHS over LHS. * * LHS.ReverseMerge(RHS): * 1. Dict <- Dict: * - Copy all nonconflicting key-value pairs from RHS to LHS. * - For every pair of conflicting values apply LHS[key].ReverseMerge(RHS[key]). * 2. Null <- Anything: * - Copy RHS over LHS. * 3. Other conflicts: * - Do nothing. */ TValue& MergeUpdateJson(TStringBuf json, TMaybe mergeOptions = {}); // returns self TValue& ReverseMergeJson(TStringBuf json, TMaybe mergeOptions = {}); // returns self static bool MergeUpdateJson(TValue&, TStringBuf json, TMaybe mergeOptions = {}); // returns true unless failed to parse the json static bool ReverseMergeJson(TValue&, TStringBuf json, TMaybe mergeOptions = {}); // returns true unless failed to parse the json TValue& MergeUpdate(const TValue& delta, TMaybe mergeOptions = {}); // return self TValue& ReverseMerge(const TValue& delta, TMaybe mergeOptions = {}); // return self public: // Path methods ///////////////////////////////////////////////////////// // TODO: add throwing variants // make sure to properly escape the tokens static TString EscapeForPath(TStringBuf rawKey); // converts a raw dict key into a valid token for a selector path static bool PathValid(TStringBuf path); // returns true if the path is syntactically valid bool PathExists(TStringBuf path) const; // returns true if the path is syntactically valid and the target value exists const TValue& TrySelect(TStringBuf path) const; // returns the target value // if the path is syntactically valid and the target value exists // otherwise returns NSc::Null() TValue* TrySelectOrAdd(TStringBuf path); // returns the target value if it exists or creates if not // if the path is syntactically valid // otherwise returns NSc::Null() TValue TrySelectAndDelete(TStringBuf path); // deletes and returns the target value // if the path is syntactically valid and the target value existed // otherwise returns NSc::Null() public: // Copy methods ///////////////////////////////////////////////////////// TValue Clone() const; // returns deep copy of self (on the separate pool) TValue& CopyFrom(const TValue& other); // deep copy other value into self, returns self TValue CreateNew() const; // returns a new null value which shares a memory pool with self TValue& Swap(TValue& v); static bool Same(const TValue&, const TValue&); // point to the same core static bool Equal(const TValue&, const TValue&); // recursively equal static bool SamePool(const TValue&, const TValue&); // share arena public: // very specific methods useful in very specific corner cases static TValue From(const ::google::protobuf::Message&, bool mapAsDict = false); void To(::google::protobuf::Message&, const TProtoOpts& opts = {}) const; public: inline explicit TValue(TPoolPtr&); static const TScCore& DefaultCore(); static const TArray& DefaultArray(); static const TDict& DefaultDict(); static const TValue& DefaultValue(); static const TValue& Null() { return DefaultValue(); } void DoWriteJsonImpl(IOutputStream&, const TJsonOpts&, NImpl::TKeySortContext&, NImpl::TSelfLoopContext&) const; bool IsSameOrAncestorOf(const TValue& other) const; private: TValue& DoMerge(const TValue& delta, bool olddelta, TMaybe mergeOptions); TValue& DoMergeImpl(const TValue& delta, bool olddelta, TMaybe mergeOptions, NImpl::TSelfLoopContext&, NImpl::TSelfOverrideContext&); TValue& DoCopyFromImpl(const TValue& other, NImpl::TSelfLoopContext&, NImpl::TSelfOverrideContext&); NJson::TJsonValue ToJsonValueImpl(NImpl::TSelfLoopContext&) const; bool IsSameOrAncestorOfImpl(const TScCore& other, NImpl::TSelfLoopContext& loopCtx) const; inline TScCore& CoreMutable(); inline TScCore& CoreMutableForSet(); inline const TScCore& Core() const; static inline TScCore* NewCore(TPoolPtr&); static TValue FromField(const ::google::protobuf::Message&, const ::google::protobuf::FieldDescriptor*); static TValue FromRepeatedField(const ::google::protobuf::Message&, const ::google::protobuf::FieldDescriptor*, int index); void ValueToField(const TValue& value, ::google::protobuf::Message&, const ::google::protobuf::FieldDescriptor*, const TProtoOpts& opts) const; void ToField(::google::protobuf::Message&, const ::google::protobuf::FieldDescriptor*, const TProtoOpts& opts) const; void ToEnumField(::google::protobuf::Message&, const ::google::protobuf::FieldDescriptor*, const TProtoOpts& opts) const; void ToRepeatedField(::google::protobuf::Message&, const ::google::protobuf::FieldDescriptor*, const TProtoOpts& opts) const; void ToMapField(::google::protobuf::Message&, const ::google::protobuf::FieldDescriptor*, const TProtoOpts& opts) const; }; inline const TValue& Null() { return TValue::DefaultValue(); } class TArray: public TDeque, TNonCopyable { using TParent = TDeque; public: TArray(TMemoryPool* p) : TParent(p) { } template void AppendAll(TIt begin, TIt end) { TParent::insert(TParent::end(), begin, end); } template void AppendAll(TColl&& coll) { AppendAll(std::begin(coll), std::end(coll)); } void AppendAll(std::initializer_list coll) { AppendAll(coll.begin(), coll.end()); } const TValue& operator[](size_t i) const { return EnsureIndex(i); } TValue& operator[](size_t i) { return EnsureIndex(i); } const TValue& front() const { return EnsureIndex(0); } TValue& front() { return EnsureIndex(0); } const TValue& back() const { return EnsureIndex(LastIndex()); } TValue& back() { return EnsureIndex(LastIndex()); } void pop_back() { if (empty()) return; TParent::pop_back(); } void pop_front() { if (empty()) return; TParent::pop_front(); } private: size_t LastIndex() const { return ::Max(size(), 1) - 1; } TValue& EnsureIndex(size_t i) { if (i >= size()) resize(::Min(i + 1, ::Max()), TValue::DefaultValue()); return TParent::operator[](i); } const TValue& EnsureIndex(size_t i) const { return i < size() ? TParent::operator[](i) : TValue::DefaultValue(); } }; // todo: densehashtable // todo: allow insertions // todo: make TDict methods safe class TDict: public THashMap, TEqualTo, TPoolAllocator>, TNonCopyable { using TParent = THashMap, TEqualTo, TPoolAllocator>; public: TDict(TMemoryPool* p) : TParent(p) { } template const TValue& Get(const TStr& key) const { const_iterator it = find(key); return it != end() ? it->second : TValue::DefaultValue(); } }; } #include "scimpl.h" #include "scheme_cast.h" #ifdef _MSC_VER #pragma warning(default : 4521 4522) #endif