mirror of
				https://github.com/ton-blockchain/ton
				synced 2025-03-09 15:40:10 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			880 lines
		
	
	
	
		
			20 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			880 lines
		
	
	
	
		
			20 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /*
 | |
|     This file is part of TON Blockchain Library.
 | |
| 
 | |
|     TON Blockchain Library is free software: you can redistribute it and/or modify
 | |
|     it under the terms of the GNU Lesser General Public License as published by
 | |
|     the Free Software Foundation, either version 2 of the License, or
 | |
|     (at your option) any later version.
 | |
| 
 | |
|     TON Blockchain Library is distributed in the hope that it will be useful,
 | |
|     but WITHOUT ANY WARRANTY; without even the implied warranty of
 | |
|     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | |
|     GNU Lesser General Public License for more details.
 | |
| 
 | |
|     You should have received a copy of the GNU Lesser General Public License
 | |
|     along with TON Blockchain Library.  If not, see <http://www.gnu.org/licenses/>.
 | |
| 
 | |
|     Copyright 2017-2020 Telegram Systems LLP
 | |
| */
 | |
| #pragma once
 | |
| 
 | |
| #include "td/utils/common.h"
 | |
| #include "td/utils/logging.h"
 | |
| #include "td/utils/Parser.h"
 | |
| #include "td/utils/Slice.h"
 | |
| #include "td/utils/StackAllocator.h"
 | |
| #include "td/utils/Status.h"
 | |
| #include "td/utils/StringBuilder.h"
 | |
| 
 | |
| #include <new>
 | |
| #include <type_traits>
 | |
| #include <utility>
 | |
| 
 | |
| namespace td {
 | |
| 
 | |
| class JsonTrue {
 | |
|  public:
 | |
|   friend StringBuilder &operator<<(StringBuilder &sb, const JsonTrue &val) {
 | |
|     return sb << "true";
 | |
|   }
 | |
| };
 | |
| 
 | |
| class JsonFalse {
 | |
|  public:
 | |
|   friend StringBuilder &operator<<(StringBuilder &sb, const JsonFalse &val) {
 | |
|     return sb << "false";
 | |
|   }
 | |
| };
 | |
| 
 | |
| class JsonNull {
 | |
|  public:
 | |
|   friend StringBuilder &operator<<(StringBuilder &sb, JsonNull val) {
 | |
|     return sb << "null";
 | |
|   }
 | |
| };
 | |
| 
 | |
| class JsonBool {
 | |
|  public:
 | |
|   explicit JsonBool(bool value) : value_(value) {
 | |
|   }
 | |
|   friend StringBuilder &operator<<(StringBuilder &sb, const JsonBool &val) {
 | |
|     if (val.value_) {
 | |
|       return sb << JsonTrue();
 | |
|     } else {
 | |
|       return sb << JsonFalse();
 | |
|     }
 | |
|   }
 | |
| 
 | |
|  private:
 | |
|   bool value_;
 | |
| };
 | |
| 
 | |
| class JsonInt {
 | |
|  public:
 | |
|   explicit JsonInt(int32 value) : value_(value) {
 | |
|   }
 | |
|   friend StringBuilder &operator<<(StringBuilder &sb, const JsonInt &val) {
 | |
|     return sb << val.value_;
 | |
|   }
 | |
| 
 | |
|  private:
 | |
|   int32 value_;
 | |
| };
 | |
| 
 | |
| class JsonLong {
 | |
|  public:
 | |
|   explicit JsonLong(int64 value) : value_(value) {
 | |
|   }
 | |
|   friend StringBuilder &operator<<(StringBuilder &sb, const JsonLong &val) {
 | |
|     return sb << val.value_;
 | |
|   }
 | |
| 
 | |
|  private:
 | |
|   int64 value_;
 | |
| };
 | |
| 
 | |
| class JsonFloat {
 | |
|  public:
 | |
|   explicit JsonFloat(double value) : value_(value) {
 | |
|   }
 | |
|   friend StringBuilder &operator<<(StringBuilder &sb, const JsonFloat &val) {
 | |
|     return sb << val.value_;
 | |
|   }
 | |
| 
 | |
|  private:
 | |
|   double value_;
 | |
| };
 | |
| 
 | |
| class JsonOneChar {
 | |
|  public:
 | |
|   explicit JsonOneChar(unsigned int c) : c_(c) {
 | |
|   }
 | |
| 
 | |
|   friend StringBuilder &operator<<(StringBuilder &sb, const JsonOneChar &val) {
 | |
|     auto c = val.c_;
 | |
|     return sb << '\\' << 'u' << "0123456789abcdef"[c >> 12] << "0123456789abcdef"[(c >> 8) & 15]
 | |
|               << "0123456789abcdef"[(c >> 4) & 15] << "0123456789abcdef"[c & 15];
 | |
|   }
 | |
| 
 | |
|  private:
 | |
|   unsigned int c_;
 | |
| };
 | |
| 
 | |
| class JsonChar {
 | |
|  public:
 | |
|   explicit JsonChar(unsigned int c) : c_(c) {
 | |
|   }
 | |
|   friend StringBuilder &operator<<(StringBuilder &sb, const JsonChar &val) {
 | |
|     auto c = val.c_;
 | |
|     if (c < 0x10000) {
 | |
|       if (0xD7FF < c && c < 0xE000) {
 | |
|         // UTF-8 correctness has already been checked
 | |
|         UNREACHABLE();
 | |
|       }
 | |
|       return sb << JsonOneChar(c);
 | |
|     } else if (c <= 0x10ffff) {
 | |
|       return sb << JsonOneChar(0xD7C0 + (c >> 10)) << JsonOneChar(0xDC00 + (c & 0x3FF));
 | |
|     } else {
 | |
|       // UTF-8 correctness has already been checked
 | |
|       UNREACHABLE();
 | |
|     }
 | |
|   }
 | |
| 
 | |
|  private:
 | |
|   unsigned int c_;
 | |
| };
 | |
| 
 | |
| class JsonRaw {
 | |
|  public:
 | |
|   explicit JsonRaw(Slice value) : value_(value) {
 | |
|   }
 | |
|   friend StringBuilder &operator<<(StringBuilder &sb, const JsonRaw &val) {
 | |
|     return sb << val.value_;
 | |
|   }
 | |
| 
 | |
|  private:
 | |
|   Slice value_;
 | |
| };
 | |
| 
 | |
| class JsonRawString {
 | |
|  public:
 | |
|   explicit JsonRawString(Slice value) : value_(value) {
 | |
|   }
 | |
|   friend StringBuilder &operator<<(StringBuilder &sb, const JsonRawString &val);
 | |
| 
 | |
|  private:
 | |
|   Slice value_;
 | |
| };
 | |
| 
 | |
| class JsonString {
 | |
|  public:
 | |
|   explicit JsonString(Slice str) : str_(str) {
 | |
|   }
 | |
| 
 | |
|   friend StringBuilder &operator<<(StringBuilder &sb, const JsonString &val);
 | |
| 
 | |
|  private:
 | |
|   Slice str_;
 | |
| };
 | |
| 
 | |
| class JsonScope;
 | |
| class JsonValueScope;
 | |
| class JsonArrayScope;
 | |
| class JsonObjectScope;
 | |
| 
 | |
| class JsonBuilder {
 | |
|  public:
 | |
|   explicit JsonBuilder(StringBuilder &&sb = {}, int32 offset = -1) : sb_(std::move(sb)), offset_(offset) {
 | |
|   }
 | |
|   StringBuilder &string_builder() {
 | |
|     return sb_;
 | |
|   }
 | |
|   friend class JsonScope;
 | |
|   JsonValueScope enter_value() TD_WARN_UNUSED_RESULT;
 | |
|   JsonArrayScope enter_array() TD_WARN_UNUSED_RESULT;
 | |
|   JsonObjectScope enter_object() TD_WARN_UNUSED_RESULT;
 | |
| 
 | |
|   int32 offset() const {
 | |
|     return offset_;
 | |
|   }
 | |
|   bool is_pretty() const {
 | |
|     return offset_ >= 0;
 | |
|   }
 | |
|   void print_offset() {
 | |
|     if (offset_ >= 0) {
 | |
|       sb_ << '\n';
 | |
|       for (int x = 0; x < offset_; x++) {
 | |
|         sb_ << "   ";
 | |
|       }
 | |
|     }
 | |
|   }
 | |
|   void dec_offset() {
 | |
|     if (offset_ >= 0) {
 | |
|       CHECK(offset_ > 0);
 | |
|       offset_--;
 | |
|     }
 | |
|   }
 | |
|   void inc_offset() {
 | |
|     if (offset_ >= 0) {
 | |
|       offset_++;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|  private:
 | |
|   StringBuilder sb_;
 | |
|   JsonScope *scope_ = nullptr;
 | |
|   int32 offset_;
 | |
| };
 | |
| 
 | |
| class Jsonable {};
 | |
| 
 | |
| class JsonScope {
 | |
|  public:
 | |
|   explicit JsonScope(JsonBuilder *jb) : sb_(&jb->sb_), jb_(jb), save_scope_(jb->scope_) {
 | |
|     jb_->scope_ = this;
 | |
|     CHECK(is_active());
 | |
|   }
 | |
|   JsonScope(const JsonScope &other) = delete;
 | |
|   JsonScope(JsonScope &&other) : sb_(other.sb_), jb_(other.jb_), save_scope_(other.save_scope_) {
 | |
|     other.jb_ = nullptr;
 | |
|   }
 | |
|   JsonScope &operator=(const JsonScope &) = delete;
 | |
|   JsonScope &operator=(JsonScope &&) = delete;
 | |
|   ~JsonScope() {
 | |
|     if (jb_) {
 | |
|       leave();
 | |
|     }
 | |
|   }
 | |
|   void leave() {
 | |
|     CHECK(is_active());
 | |
|     jb_->scope_ = save_scope_;
 | |
|   }
 | |
| 
 | |
|  protected:
 | |
|   StringBuilder *sb_;
 | |
| 
 | |
|   // For CHECK
 | |
|   JsonBuilder *jb_;
 | |
|   JsonScope *save_scope_;
 | |
| 
 | |
|   bool is_active() const {
 | |
|     return jb_ && jb_->scope_ == this;
 | |
|   }
 | |
| 
 | |
|   JsonScope &operator<<(JsonTrue x) {
 | |
|     *sb_ << x;
 | |
|     return *this;
 | |
|   }
 | |
|   JsonScope &operator<<(JsonFalse x) {
 | |
|     *sb_ << x;
 | |
|     return *this;
 | |
|   }
 | |
|   JsonScope &operator<<(JsonNull x) {
 | |
|     *sb_ << x;
 | |
|     return *this;
 | |
|   }
 | |
|   JsonScope &operator<<(const JsonBool &x) {
 | |
|     *sb_ << x;
 | |
|     return *this;
 | |
|   }
 | |
|   JsonScope &operator<<(const JsonInt &x) {
 | |
|     *sb_ << x;
 | |
|     return *this;
 | |
|   }
 | |
|   JsonScope &operator<<(const JsonLong &x) {
 | |
|     *sb_ << x;
 | |
|     return *this;
 | |
|   }
 | |
|   JsonScope &operator<<(const JsonFloat &x) {
 | |
|     *sb_ << x;
 | |
|     return *this;
 | |
|   }
 | |
|   JsonScope &operator<<(const JsonString &x) {
 | |
|     *sb_ << x;
 | |
|     return *this;
 | |
|   }
 | |
|   JsonScope &operator<<(const JsonRawString &x) {
 | |
|     *sb_ << x;
 | |
|     return *this;
 | |
|   }
 | |
|   JsonScope &operator<<(const JsonRaw &x) {
 | |
|     *sb_ << x;
 | |
|     return *this;
 | |
|   }
 | |
|   JsonScope &operator<<(bool x) = delete;
 | |
|   JsonScope &operator<<(int32 x) {
 | |
|     return *this << JsonInt(x);
 | |
|   }
 | |
|   JsonScope &operator<<(int64 x) {
 | |
|     return *this << JsonLong(x);
 | |
|   }
 | |
|   JsonScope &operator<<(double x) {
 | |
|     return *this << JsonFloat(x);
 | |
|   }
 | |
|   template <size_t N>
 | |
|   JsonScope &operator<<(const char (&x)[N]) {
 | |
|     return *this << JsonString(Slice(x));
 | |
|   }
 | |
|   JsonScope &operator<<(const char *x) {
 | |
|     return *this << JsonString(Slice(x));
 | |
|   }
 | |
|   JsonScope &operator<<(Slice x) {
 | |
|     return *this << JsonString(x);
 | |
|   }
 | |
| };
 | |
| 
 | |
| class JsonValueScope : public JsonScope {
 | |
|  public:
 | |
|   using JsonScope::JsonScope;
 | |
|   template <class T>
 | |
|   std::enable_if_t<std::is_base_of<Jsonable, typename std::decay<T>::type>::value, JsonValueScope &> operator<<(
 | |
|       const T &x) {
 | |
|     x.store(this);
 | |
|     return *this;
 | |
|   }
 | |
|   template <class T>
 | |
|   std::enable_if_t<!std::is_base_of<Jsonable, typename std::decay<T>::type>::value, JsonValueScope &> operator<<(
 | |
|       const T &x) {
 | |
|     CHECK(!was_);
 | |
|     was_ = true;
 | |
|     JsonScope::operator<<(x);
 | |
|     return *this;
 | |
|   }
 | |
| 
 | |
|   JsonArrayScope enter_array() TD_WARN_UNUSED_RESULT;
 | |
|   JsonObjectScope enter_object() TD_WARN_UNUSED_RESULT;
 | |
| 
 | |
|  private:
 | |
|   bool was_ = false;
 | |
| };
 | |
| 
 | |
| class JsonArrayScope : public JsonScope {
 | |
|  public:
 | |
|   explicit JsonArrayScope(JsonBuilder *jb) : JsonScope(jb) {
 | |
|     jb->inc_offset();
 | |
|     *sb_ << "[";
 | |
|   }
 | |
|   JsonArrayScope(JsonArrayScope &&other) = default;
 | |
|   ~JsonArrayScope() {
 | |
|     if (jb_) {
 | |
|       leave();
 | |
|     }
 | |
|   }
 | |
|   void leave() {
 | |
|     jb_->dec_offset();
 | |
|     if (jb_->is_pretty()) {
 | |
|       jb_->print_offset();
 | |
|     }
 | |
|     *sb_ << "]";
 | |
|   }
 | |
|   template <class T>
 | |
|   JsonArrayScope &operator<<(const T &x) {
 | |
|     return (*this)(x);
 | |
|   }
 | |
|   template <class T>
 | |
|   JsonArrayScope &operator()(const T &x) {
 | |
|     enter_value() << x;
 | |
|     return *this;
 | |
|   }
 | |
|   JsonValueScope enter_value() {
 | |
|     CHECK(is_active());
 | |
|     if (is_first_) {
 | |
|       *sb_ << ",";
 | |
|     } else {
 | |
|       is_first_ = true;
 | |
|     }
 | |
|     if (jb_->is_pretty()) {
 | |
|       jb_->print_offset();
 | |
|     }
 | |
|     return jb_->enter_value();
 | |
|   }
 | |
| 
 | |
|  private:
 | |
|   bool is_first_ = false;
 | |
| };
 | |
| 
 | |
| class JsonObjectScope : public JsonScope {
 | |
|  public:
 | |
|   explicit JsonObjectScope(JsonBuilder *jb) : JsonScope(jb) {
 | |
|     jb->inc_offset();
 | |
|     *sb_ << "{";
 | |
|   }
 | |
|   JsonObjectScope(JsonObjectScope &&other) = default;
 | |
|   ~JsonObjectScope() {
 | |
|     if (jb_) {
 | |
|       leave();
 | |
|     }
 | |
|   }
 | |
|   void leave() {
 | |
|     jb_->dec_offset();
 | |
|     if (jb_->is_pretty()) {
 | |
|       jb_->print_offset();
 | |
|     }
 | |
|     *sb_ << "}";
 | |
|   }
 | |
|   template <class T>
 | |
|   JsonObjectScope &operator()(Slice key, T &&value) {
 | |
|     CHECK(is_active());
 | |
|     if (is_first_) {
 | |
|       *sb_ << ",";
 | |
|     } else {
 | |
|       is_first_ = true;
 | |
|     }
 | |
|     if (jb_->is_pretty()) {
 | |
|       jb_->print_offset();
 | |
|     }
 | |
|     jb_->enter_value() << key;
 | |
|     if (jb_->is_pretty()) {
 | |
|       *sb_ << " : ";
 | |
|     } else {
 | |
|       *sb_ << ":";
 | |
|     }
 | |
|     jb_->enter_value() << value;
 | |
|     return *this;
 | |
|   }
 | |
|   JsonObjectScope &operator<<(const JsonRaw &key_value) {
 | |
|     CHECK(is_active());
 | |
|     is_first_ = true;
 | |
|     jb_->enter_value() << key_value;
 | |
|     return *this;
 | |
|   }
 | |
| 
 | |
|  private:
 | |
|   bool is_first_ = false;
 | |
| };
 | |
| 
 | |
| inline JsonArrayScope JsonValueScope::enter_array() {
 | |
|   CHECK(!was_);
 | |
|   was_ = true;
 | |
|   return JsonArrayScope(jb_);
 | |
| }
 | |
| inline JsonObjectScope JsonValueScope::enter_object() {
 | |
|   CHECK(!was_);
 | |
|   was_ = true;
 | |
|   return JsonObjectScope(jb_);
 | |
| }
 | |
| inline JsonValueScope JsonBuilder::enter_value() {
 | |
|   return JsonValueScope(this);
 | |
| }
 | |
| inline JsonObjectScope JsonBuilder::enter_object() {
 | |
|   return JsonObjectScope(this);
 | |
| }
 | |
| inline JsonArrayScope JsonBuilder::enter_array() {
 | |
|   return JsonArrayScope(this);
 | |
| }
 | |
| 
 | |
| class JsonValue;
 | |
| 
 | |
| using JsonObject = vector<std::pair<MutableSlice, JsonValue>>;
 | |
| using JsonArray = vector<JsonValue>;
 | |
| 
 | |
| class JsonValue : public Jsonable {
 | |
|  public:
 | |
|   enum class Type { Null, Number, Boolean, String, Array, Object };
 | |
| 
 | |
|   static Slice get_type_name(Type type);
 | |
| 
 | |
|   JsonValue() {
 | |
|   }
 | |
|   ~JsonValue() {
 | |
|     destroy();
 | |
|   }
 | |
|   JsonValue(JsonValue &&other) : JsonValue() {
 | |
|     init(std::move(other));
 | |
|   }
 | |
|   JsonValue &operator=(JsonValue &&other) {
 | |
|     if (&other == this) {
 | |
|       return *this;
 | |
|     }
 | |
|     destroy();
 | |
|     init(std::move(other));
 | |
|     return *this;
 | |
|   }
 | |
|   JsonValue(const JsonValue &other) = delete;
 | |
|   JsonValue &operator=(const JsonValue &other) = delete;
 | |
| 
 | |
|   Type type() const {
 | |
|     return type_;
 | |
|   }
 | |
| 
 | |
|   MutableSlice &get_string() {
 | |
|     CHECK(type_ == Type::String);
 | |
|     return string_;
 | |
|   }
 | |
|   const MutableSlice &get_string() const {
 | |
|     CHECK(type_ == Type::String);
 | |
|     return string_;
 | |
|   }
 | |
|   bool &get_boolean() {
 | |
|     CHECK(type_ == Type::Boolean);
 | |
|     return boolean_;
 | |
|   }
 | |
|   const bool &get_boolean() const {
 | |
|     CHECK(type_ == Type::Boolean);
 | |
|     return boolean_;
 | |
|   }
 | |
| 
 | |
|   MutableSlice &get_number() {
 | |
|     CHECK(type_ == Type::Number);
 | |
|     return number_;
 | |
|   }
 | |
|   const MutableSlice &get_number() const {
 | |
|     CHECK(type_ == Type::Number);
 | |
|     return number_;
 | |
|   }
 | |
| 
 | |
|   JsonArray &get_array() {
 | |
|     CHECK(type_ == Type::Array);
 | |
|     return array_;
 | |
|   }
 | |
|   const JsonArray &get_array() const {
 | |
|     CHECK(type_ == Type::Array);
 | |
|     return array_;
 | |
|   }
 | |
| 
 | |
|   JsonObject &get_object() {
 | |
|     CHECK(type_ == Type::Object);
 | |
|     return object_;
 | |
|   }
 | |
|   const JsonObject &get_object() const {
 | |
|     CHECK(type_ == Type::Object);
 | |
|     return object_;
 | |
|   }
 | |
| 
 | |
|   static JsonValue create_boolean(bool val) {
 | |
|     JsonValue res;
 | |
|     res.init_boolean(val);
 | |
|     return res;
 | |
|   }
 | |
| 
 | |
|   static JsonValue create_number(MutableSlice number) {
 | |
|     JsonValue res;
 | |
|     res.init_number(number);
 | |
|     return res;
 | |
|   }
 | |
| 
 | |
|   static JsonValue create_string(MutableSlice str) {
 | |
|     JsonValue res;
 | |
|     res.init_string(str);
 | |
|     return res;
 | |
|   }
 | |
| 
 | |
|   static JsonValue create_array(JsonArray v) {
 | |
|     JsonValue res;
 | |
|     res.init_array(std::move(v));
 | |
|     return res;
 | |
|   }
 | |
| 
 | |
|   static JsonValue make_object(JsonObject c) {
 | |
|     JsonValue res;
 | |
|     res.init_object(std::move(c));
 | |
|     return res;
 | |
|   }
 | |
| 
 | |
|   void store(JsonValueScope *scope) const {
 | |
|     switch (type_) {
 | |
|       case Type::Null:
 | |
|         *scope << JsonRaw("null");
 | |
|         break;
 | |
|       case Type::Boolean:
 | |
|         if (get_boolean()) {
 | |
|           *scope << JsonRaw("true");
 | |
|         } else {
 | |
|           *scope << JsonRaw("false");
 | |
|         }
 | |
|         break;
 | |
|       case Type::Number:
 | |
|         *scope << JsonRaw(get_number());
 | |
|         break;
 | |
|       case Type::String:
 | |
|         *scope << JsonString(get_string());
 | |
|         break;
 | |
|       case Type::Array: {
 | |
|         auto arr = scope->enter_array();
 | |
|         for (auto &val : get_array()) {
 | |
|           arr << val;
 | |
|         }
 | |
|         break;
 | |
|       }
 | |
|       case Type::Object: {
 | |
|         auto object = scope->enter_object();
 | |
|         for (auto &key_value : get_object()) {
 | |
|           object(key_value.first, key_value.second);
 | |
|         }
 | |
|         break;
 | |
|       }
 | |
|     }
 | |
|   };
 | |
| 
 | |
|  private:
 | |
|   Type type_{Type::Null};
 | |
|   union {
 | |
|     MutableSlice number_;
 | |
|     bool boolean_;
 | |
|     MutableSlice string_;
 | |
|     JsonArray array_;
 | |
|     JsonObject object_;
 | |
|   };
 | |
| 
 | |
|   void init_null() {
 | |
|     type_ = Type::Null;
 | |
|   }
 | |
|   void init_number(MutableSlice number) {
 | |
|     type_ = Type::Number;
 | |
|     new (&number_) MutableSlice(number);
 | |
|   }
 | |
|   void init_boolean(bool boolean) {
 | |
|     type_ = Type::Boolean;
 | |
|     boolean_ = boolean;
 | |
|   }
 | |
|   void init_string(MutableSlice slice) {
 | |
|     type_ = Type::String;
 | |
|     new (&string_) MutableSlice(slice);
 | |
|   }
 | |
|   void init_array(JsonArray array) {
 | |
|     type_ = Type::Array;
 | |
|     new (&array_) JsonArray(std::move(array));
 | |
|   }
 | |
|   void init_object(JsonObject object) {
 | |
|     type_ = Type::Object;
 | |
|     new (&object_) JsonObject(std::move(object));
 | |
|   }
 | |
| 
 | |
|   void init(JsonValue &&other) {
 | |
|     switch (other.type_) {
 | |
|       case Type::Null:
 | |
|         break;
 | |
|       case Type::Number:
 | |
|         init_number(other.number_);
 | |
|         break;
 | |
|       case Type::Boolean:
 | |
|         init_boolean(other.boolean_);
 | |
|         break;
 | |
|       case Type::String:
 | |
|         init_string(other.string_);
 | |
|         break;
 | |
|       case Type::Array:
 | |
|         init_array(std::move(other.array_));
 | |
|         break;
 | |
|       case Type::Object:
 | |
|         init_object(std::move(other.object_));
 | |
|         break;
 | |
|     }
 | |
|     other.destroy();
 | |
|   }
 | |
| 
 | |
|   void destroy() {
 | |
|     switch (type_) {
 | |
|       case Type::Null:
 | |
|       case Type::Boolean:
 | |
|         break;
 | |
|       case Type::Number:
 | |
|         number_.~MutableSlice();
 | |
|         break;
 | |
|       case Type::String:
 | |
|         string_.~MutableSlice();
 | |
|         break;
 | |
|       case Type::Array:
 | |
|         array_.~vector<JsonValue>();
 | |
|         break;
 | |
|       case Type::Object:
 | |
|         object_.~vector<std::pair<MutableSlice, JsonValue>>();
 | |
|         break;
 | |
|     }
 | |
|     type_ = Type::Null;
 | |
|   }
 | |
| };
 | |
| 
 | |
| inline StringBuilder &operator<<(StringBuilder &sb, JsonValue::Type type) {
 | |
|   switch (type) {
 | |
|     case JsonValue::Type::Null:
 | |
|       return sb << "Null";
 | |
|     case JsonValue::Type::Number:
 | |
|       return sb << "Number";
 | |
|     case JsonValue::Type::Boolean:
 | |
|       return sb << "Boolean";
 | |
|     case JsonValue::Type::String:
 | |
|       return sb << "String";
 | |
|     case JsonValue::Type::Array:
 | |
|       return sb << "Array";
 | |
|     case JsonValue::Type::Object:
 | |
|       return sb << "Object";
 | |
|     default:
 | |
|       UNREACHABLE();
 | |
|       return sb;
 | |
|   }
 | |
| }
 | |
| 
 | |
| class VirtuallyJsonable : public Jsonable {
 | |
|  public:
 | |
|   virtual void store(JsonValueScope *scope) const = 0;
 | |
|   VirtuallyJsonable() = default;
 | |
|   VirtuallyJsonable(const VirtuallyJsonable &) = delete;
 | |
|   VirtuallyJsonable &operator=(const VirtuallyJsonable &) = delete;
 | |
|   VirtuallyJsonable(VirtuallyJsonable &&) = default;
 | |
|   VirtuallyJsonable &operator=(VirtuallyJsonable &&) = default;
 | |
|   virtual ~VirtuallyJsonable() = default;
 | |
| };
 | |
| 
 | |
| class VirtuallyJsonableInt : public VirtuallyJsonable {
 | |
|  public:
 | |
|   explicit VirtuallyJsonableInt(int32 value) : value_(value) {
 | |
|   }
 | |
|   void store(JsonValueScope *scope) const override {
 | |
|     *scope << JsonInt(value_);
 | |
|   }
 | |
| 
 | |
|  private:
 | |
|   int32 value_;
 | |
| };
 | |
| 
 | |
| class VirtuallyJsonableLong : public VirtuallyJsonable {
 | |
|  public:
 | |
|   explicit VirtuallyJsonableLong(int64 value) : value_(value) {
 | |
|   }
 | |
|   void store(JsonValueScope *scope) const override {
 | |
|     *scope << JsonLong(value_);
 | |
|   }
 | |
| 
 | |
|  private:
 | |
|   int64 value_;
 | |
| };
 | |
| 
 | |
| class VirtuallyJsonableString : public VirtuallyJsonable {
 | |
|  public:
 | |
|   explicit VirtuallyJsonableString(Slice value) : value_(value) {
 | |
|   }
 | |
|   void store(JsonValueScope *scope) const override {
 | |
|     *scope << JsonString(value_);
 | |
|   }
 | |
| 
 | |
|  private:
 | |
|   Slice value_;
 | |
| };
 | |
| 
 | |
| Result<MutableSlice> json_string_decode(Parser &parser) TD_WARN_UNUSED_RESULT;
 | |
| Status json_string_skip(Parser &parser) TD_WARN_UNUSED_RESULT;
 | |
| 
 | |
| Result<JsonValue> do_json_decode(Parser &parser, int32 max_depth) TD_WARN_UNUSED_RESULT;
 | |
| Status do_json_skip(Parser &parser, int32 max_depth) TD_WARN_UNUSED_RESULT;
 | |
| 
 | |
| inline Result<JsonValue> json_decode(MutableSlice json) {
 | |
|   Parser parser(json);
 | |
|   const int32 DEFAULT_MAX_DEPTH = 100;
 | |
|   auto result = do_json_decode(parser, DEFAULT_MAX_DEPTH);
 | |
|   if (result.is_ok()) {
 | |
|     parser.skip_whitespaces();
 | |
|     if (!parser.empty()) {
 | |
|       return Status::Error("Expected string end");
 | |
|     }
 | |
|   }
 | |
|   return result;
 | |
| }
 | |
| 
 | |
| template <class StrT, class ValT>
 | |
| StrT json_encode(const ValT &val, bool pretty = false) {
 | |
|   auto buf_len = 1 << 18;
 | |
|   auto buf = StackAllocator::alloc(buf_len);
 | |
|   JsonBuilder jb(StringBuilder(buf.as_slice(), true), pretty ? 0 : -1);
 | |
|   jb.enter_value() << val;
 | |
|   if (pretty) {
 | |
|     jb.string_builder() << "\n";
 | |
|   }
 | |
|   LOG_IF(ERROR, jb.string_builder().is_error()) << "JSON buffer overflow";
 | |
|   auto slice = jb.string_builder().as_cslice();
 | |
|   return StrT(slice.begin(), slice.size());
 | |
| }
 | |
| 
 | |
| template <class T>
 | |
| class ToJsonImpl : public Jsonable {
 | |
|  public:
 | |
|   explicit ToJsonImpl(const T &value) : value_(value) {
 | |
|   }
 | |
|   void store(JsonValueScope *scope) const {
 | |
|     to_json(*scope, value_);
 | |
|   }
 | |
| 
 | |
|  private:
 | |
|   const T &value_;
 | |
| };
 | |
| 
 | |
| template <class T>
 | |
| auto ToJson(const T &value) {
 | |
|   return ToJsonImpl<T>(value);
 | |
| }
 | |
| 
 | |
| template <class T>
 | |
| void to_json(JsonValueScope &jv, const T &value) {
 | |
|   jv << value;
 | |
| }
 | |
| 
 | |
| template <class F>
 | |
| class JsonObjectImpl : Jsonable {
 | |
|  public:
 | |
|   explicit JsonObjectImpl(F &&f) : f_(std::forward<F>(f)) {
 | |
|   }
 | |
|   void store(JsonValueScope *scope) const {
 | |
|     auto object = scope->enter_object();
 | |
|     f_(object);
 | |
|   }
 | |
| 
 | |
|  private:
 | |
|   F f_;
 | |
| };
 | |
| 
 | |
| template <class F>
 | |
| auto json_object(F &&f) {
 | |
|   return JsonObjectImpl<F>(std::forward<F>(f));
 | |
| }
 | |
| 
 | |
| template <class F>
 | |
| class JsonArrayImpl : Jsonable {
 | |
|  public:
 | |
|   explicit JsonArrayImpl(F &&f) : f_(std::forward<F>(f)) {
 | |
|   }
 | |
|   void store(JsonValueScope *scope) const {
 | |
|     auto array = scope->enter_array();
 | |
|     f_(array);
 | |
|   }
 | |
| 
 | |
|  private:
 | |
|   F f_;
 | |
| };
 | |
| 
 | |
| template <class F>
 | |
| auto json_array(F &&f) {
 | |
|   return JsonArrayImpl<F>(std::forward<F>(f));
 | |
| }
 | |
| 
 | |
| template <class A, class F>
 | |
| auto json_array(const A &a, F &&f) {
 | |
|   return json_array([&a, &f](auto &arr) {
 | |
|     for (auto &x : a) {
 | |
|       arr(f(x));
 | |
|     }
 | |
|   });
 | |
| }
 | |
| 
 | |
| bool has_json_object_field(const JsonObject &object, Slice name);
 | |
| 
 | |
| JsonValue get_json_object_field_force(JsonObject &object, Slice name) TD_WARN_UNUSED_RESULT;
 | |
| 
 | |
| Result<JsonValue> get_json_object_field(JsonObject &object, Slice name, JsonValue::Type type,
 | |
|                                         bool is_optional = true) TD_WARN_UNUSED_RESULT;
 | |
| 
 | |
| Result<bool> get_json_object_bool_field(JsonObject &object, Slice name, bool is_optional = true,
 | |
|                                         bool default_value = false) TD_WARN_UNUSED_RESULT;
 | |
| 
 | |
| Result<int32> get_json_object_int_field(JsonObject &object, Slice name, bool is_optional = true,
 | |
|                                         int32 default_value = 0) TD_WARN_UNUSED_RESULT;
 | |
| 
 | |
| Result<int64> get_json_object_long_field(JsonObject &object, Slice name, bool is_optional = true,
 | |
|                                          int64 default_value = 0) TD_WARN_UNUSED_RESULT;
 | |
| 
 | |
| Result<double> get_json_object_double_field(JsonObject &object, Slice name, bool is_optional = true,
 | |
|                                             double default_value = 0.0) TD_WARN_UNUSED_RESULT;
 | |
| 
 | |
| Result<string> get_json_object_string_field(JsonObject &object, Slice name, bool is_optional = true,
 | |
|                                             string default_value = "") TD_WARN_UNUSED_RESULT;
 | |
| 
 | |
| }  // namespace td
 |