aboutsummaryrefslogtreecommitdiffstats
path: root/library/cpp/scheme/scheme.h
blob: ca69752b7d135d1e4f969345d2c484c51251fa7c (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
#pragma once

#include "scimpl_defs.h"

#include "fwd.h"

#include <iterator>
#include <utility>

namespace NSc {
#ifdef _MSC_VER
#pragma warning(disable : 4521 4522) 
#endif

    // 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<TScCore>;
        using TPoolPtr = TIntrusivePtr<NDefinitions::TPool>;

        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:
        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 <class T> // ui16 or TStringBuf
        inline TValue& operator[](const T& idx) { 
            return GetOrAdd(idx); 
        } 

        template <class T> // 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 <class T>
        TValue& Push(T&& t) {
            return Push() = std::forward<T>(t);
        } // returns new child

        TValue& Insert(ui16 idx) { 
            return InsertUnsafe(idx); 
        } // creates missing values, returns new child 

        template <class T>
        TValue& Insert(ui16 idx, T&& v) {
            return InsertUnsafe(idx, std::forward<T>(v));
        } // creates missing values, returns new child 

        template <class TIt>
        inline TValue& AppendAll(TIt begin, TIt end); // Append(vec.begin(), vec.end())

        template <class TColl>
        inline TValue& AppendAll(TColl&& coll); // Append(vec) 

        inline TValue& AppendAll(std::initializer_list<TValue> 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 <class T>
        TValue& InsertUnsafe(size_t idx, T&& t) {
            return InsertUnsafe(idx) = std::forward<T>(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 <class T>
        TValue& Add(TStringBuf idx, T&& t) {
            return Add(idx) = std::forward<T>(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<std::pair<TStringBuf, TValue>> 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;

        static TValue FromJson(TStringBuf, const TJsonOpts& = TJsonOpts());
        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);  // returns self
        TValue& ReverseMergeJson(TStringBuf json); // returns self

        static bool MergeUpdateJson(TValue&, TStringBuf json);  // returns true unless failed to parse the json
        static bool ReverseMergeJson(TValue&, TStringBuf json); // returns true unless failed to parse the json

        TValue& MergeUpdate(const TValue& delta);  // return self 
        TValue& ReverseMerge(const TValue& delta); // 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& 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);
        TValue& DoMergeImpl(const TValue& delta, bool olddelta, 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<TValue, TPoolAllocator>, TNonCopyable { 
        using TParent = TDeque<TValue, TPoolAllocator>; 

    public:
        TArray(TMemoryPool* p)
            : TParent(p)
        { 
        } 

        template <class TIt>
        void AppendAll(TIt begin, TIt end) {
            TParent::insert(TParent::end(), begin, end);
        }

        template <class TColl>
        void AppendAll(TColl&& coll) {
            AppendAll(std::begin(coll), std::end(coll));
        }

        void AppendAll(std::initializer_list<TValue> 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_t>(size(), 1) - 1;
        }

        TValue& EnsureIndex(size_t i) {
            if (i >= size())
                resize(::Min<size_t>(i + 1, ::Max<ui16>()), 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<TStringBuf, TValue, THash<TStringBuf>, TEqualTo<TStringBuf>, TPoolAllocator>, TNonCopyable { 
        using TParent = THashMap<TStringBuf, TValue, THash<TStringBuf>, TEqualTo<TStringBuf>, TPoolAllocator>;
 
    public:
        TDict(TMemoryPool* p)
            : TParent(p)
        { 
        } 

        template <class TStr>
        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