mirror of
				https://github.com/ton-blockchain/ton
				synced 2025-03-09 15:40:10 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			813 lines
		
	
	
	
		
			21 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			813 lines
		
	
	
	
		
			21 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/port/thread_local.h"
 | 
						|
#include "td/utils/Slice.h"
 | 
						|
 | 
						|
#include <atomic>
 | 
						|
#include <limits>
 | 
						|
#include <memory>
 | 
						|
 | 
						|
namespace td {
 | 
						|
 | 
						|
struct BufferRaw {
 | 
						|
  explicit BufferRaw(size_t size) : data_size_(size) {
 | 
						|
  }
 | 
						|
  size_t data_size_;
 | 
						|
 | 
						|
  // Constant after first reader is created.
 | 
						|
  // May be change by writer before it.
 | 
						|
  // So writer may do prepends till there is no reader created.
 | 
						|
  size_t begin_ = 0;
 | 
						|
 | 
						|
  // Write by writer.
 | 
						|
  // Read by reader.
 | 
						|
  std::atomic<size_t> end_{0};
 | 
						|
 | 
						|
  mutable std::atomic<int32> ref_cnt_{1};
 | 
						|
  std::atomic<bool> has_writer_{true};
 | 
						|
  bool was_reader_{false};
 | 
						|
 | 
						|
  alignas(4) unsigned char data_[1];
 | 
						|
};
 | 
						|
 | 
						|
class BufferAllocator {
 | 
						|
 public:
 | 
						|
  class DeleteWriterPtr {
 | 
						|
   public:
 | 
						|
    void operator()(BufferRaw *ptr) {
 | 
						|
      ptr->has_writer_.store(false, std::memory_order_release);
 | 
						|
      dec_ref_cnt(ptr);
 | 
						|
    }
 | 
						|
  };
 | 
						|
  class DeleteReaderPtr {
 | 
						|
   public:
 | 
						|
    void operator()(BufferRaw *ptr) {
 | 
						|
      dec_ref_cnt(ptr);
 | 
						|
    }
 | 
						|
  };
 | 
						|
 | 
						|
  using WriterPtr = std::unique_ptr<BufferRaw, DeleteWriterPtr>;
 | 
						|
  using ReaderPtr = std::unique_ptr<BufferRaw, DeleteReaderPtr>;
 | 
						|
 | 
						|
  static WriterPtr create_writer(size_t size);
 | 
						|
 | 
						|
  static WriterPtr create_writer(size_t size, size_t prepend, size_t append);
 | 
						|
 | 
						|
  static ReaderPtr create_reader(size_t size);
 | 
						|
 | 
						|
  static ReaderPtr create_reader(const WriterPtr &raw);
 | 
						|
 | 
						|
  static ReaderPtr create_reader(const ReaderPtr &raw);
 | 
						|
 | 
						|
  static size_t get_buffer_mem();
 | 
						|
 | 
						|
  static void clear_thread_local();
 | 
						|
 | 
						|
 private:
 | 
						|
  static ReaderPtr create_reader_fast(size_t size);
 | 
						|
 | 
						|
  static WriterPtr create_writer_exact(size_t size);
 | 
						|
 | 
						|
  struct BufferRawDeleter {
 | 
						|
    void operator()(BufferRaw *ptr) {
 | 
						|
      dec_ref_cnt(ptr);
 | 
						|
    }
 | 
						|
  };
 | 
						|
  struct BufferRawTls {
 | 
						|
    std::unique_ptr<BufferRaw, BufferRawDeleter> buffer_raw;
 | 
						|
  };
 | 
						|
 | 
						|
  static TD_THREAD_LOCAL BufferRawTls *buffer_raw_tls;
 | 
						|
 | 
						|
  static void dec_ref_cnt(BufferRaw *ptr);
 | 
						|
 | 
						|
  static BufferRaw *create_buffer_raw(size_t size);
 | 
						|
 | 
						|
  static std::atomic<size_t> buffer_mem;
 | 
						|
};
 | 
						|
 | 
						|
using BufferWriterPtr = BufferAllocator::WriterPtr;
 | 
						|
using BufferReaderPtr = BufferAllocator::ReaderPtr;
 | 
						|
 | 
						|
class BufferSlice {
 | 
						|
 public:
 | 
						|
  BufferSlice() = default;
 | 
						|
  explicit BufferSlice(BufferReaderPtr buffer_ptr) : buffer_(std::move(buffer_ptr)) {
 | 
						|
    if (is_null()) {
 | 
						|
      return;
 | 
						|
    }
 | 
						|
    begin_ = buffer_->begin_;
 | 
						|
    sync_with_writer();
 | 
						|
  }
 | 
						|
  BufferSlice(BufferReaderPtr buffer_ptr, size_t begin, size_t end)
 | 
						|
      : buffer_(std::move(buffer_ptr)), begin_(begin), end_(end) {
 | 
						|
  }
 | 
						|
 | 
						|
  explicit BufferSlice(size_t size) : buffer_(BufferAllocator::create_reader(size)) {
 | 
						|
    end_ = buffer_->end_.load(std::memory_order_relaxed);
 | 
						|
    begin_ = end_ - ((size + 7) & -8);
 | 
						|
    end_ = begin_ + size;
 | 
						|
  }
 | 
						|
 | 
						|
  explicit BufferSlice(Slice slice) : BufferSlice(slice.size()) {
 | 
						|
    as_slice().copy_from(slice);
 | 
						|
  }
 | 
						|
 | 
						|
  BufferSlice(const char *ptr, size_t size) : BufferSlice(Slice(ptr, size)) {
 | 
						|
  }
 | 
						|
 | 
						|
  BufferSlice clone() const {
 | 
						|
    if (is_null()) {
 | 
						|
      return BufferSlice(BufferReaderPtr(), begin_, end_);
 | 
						|
    }
 | 
						|
    return BufferSlice(BufferAllocator::create_reader(buffer_), begin_, end_);
 | 
						|
  }
 | 
						|
 | 
						|
  BufferSlice copy() const {
 | 
						|
    if (is_null()) {
 | 
						|
      return BufferSlice(BufferReaderPtr(), begin_, end_);
 | 
						|
    }
 | 
						|
    return BufferSlice(as_slice());
 | 
						|
  }
 | 
						|
 | 
						|
  Slice as_slice() const {
 | 
						|
    if (is_null()) {
 | 
						|
      return Slice();
 | 
						|
    }
 | 
						|
    return Slice(buffer_->data_ + begin_, size());
 | 
						|
  }
 | 
						|
 | 
						|
  operator Slice() const {
 | 
						|
    return as_slice();
 | 
						|
  }
 | 
						|
 | 
						|
  MutableSlice as_slice() {
 | 
						|
    if (is_null()) {
 | 
						|
      return MutableSlice();
 | 
						|
    }
 | 
						|
    return MutableSlice(buffer_->data_ + begin_, size());
 | 
						|
  }
 | 
						|
 | 
						|
  Slice prepare_read() const {
 | 
						|
    return as_slice();
 | 
						|
  }
 | 
						|
 | 
						|
  Slice after(size_t offset) const {
 | 
						|
    auto full = as_slice();
 | 
						|
    full.remove_prefix(offset);
 | 
						|
    return full;
 | 
						|
  }
 | 
						|
 | 
						|
  bool confirm_read(size_t size) {
 | 
						|
    begin_ += size;
 | 
						|
    CHECK(begin_ <= end_);
 | 
						|
    return begin_ == end_;
 | 
						|
  }
 | 
						|
 | 
						|
  void truncate(size_t limit) {
 | 
						|
    if (size() > limit) {
 | 
						|
      end_ = begin_ + limit;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  BufferSlice from_slice(Slice slice) const {
 | 
						|
    auto res = BufferSlice(BufferAllocator::create_reader(buffer_));
 | 
						|
    res.begin_ = static_cast<size_t>(slice.ubegin() - buffer_->data_);
 | 
						|
    res.end_ = static_cast<size_t>(slice.uend() - buffer_->data_);
 | 
						|
    CHECK(buffer_->begin_ <= res.begin_);
 | 
						|
    CHECK(res.begin_ <= res.end_);
 | 
						|
    CHECK(res.end_ <= buffer_->end_.load(std::memory_order_relaxed));
 | 
						|
    return res;
 | 
						|
  }
 | 
						|
 | 
						|
  // like in std::string
 | 
						|
  char *data() {
 | 
						|
    return as_slice().data();
 | 
						|
  }
 | 
						|
  const char *data() const {
 | 
						|
    return as_slice().data();
 | 
						|
  }
 | 
						|
  char operator[](size_t at) const {
 | 
						|
    return as_slice()[at];
 | 
						|
  }
 | 
						|
 | 
						|
  bool empty() const {
 | 
						|
    return size() == 0;
 | 
						|
  }
 | 
						|
 | 
						|
  bool is_null() const {
 | 
						|
    return !buffer_;
 | 
						|
  }
 | 
						|
 | 
						|
  size_t size() const {
 | 
						|
    if (is_null()) {
 | 
						|
      return 0;
 | 
						|
    }
 | 
						|
    return end_ - begin_;
 | 
						|
  }
 | 
						|
 | 
						|
  // like in std::string
 | 
						|
  size_t length() const {
 | 
						|
    return size();
 | 
						|
  }
 | 
						|
 | 
						|
  // set end_ into writer's end_
 | 
						|
  size_t sync_with_writer() {
 | 
						|
    CHECK(!is_null());
 | 
						|
    auto old_end = end_;
 | 
						|
    end_ = buffer_->end_.load(std::memory_order_acquire);
 | 
						|
    return end_ - old_end;
 | 
						|
  }
 | 
						|
  bool is_writer_alive() const {
 | 
						|
    CHECK(!is_null());
 | 
						|
    return buffer_->has_writer_.load(std::memory_order_acquire);
 | 
						|
  }
 | 
						|
  void clear() {
 | 
						|
    begin_ = 0;
 | 
						|
    end_ = 0;
 | 
						|
    buffer_ = nullptr;
 | 
						|
  }
 | 
						|
 | 
						|
 private:
 | 
						|
  BufferReaderPtr buffer_;
 | 
						|
  size_t begin_ = 0;
 | 
						|
  size_t end_ = 0;
 | 
						|
};
 | 
						|
 | 
						|
template <class StorerT>
 | 
						|
void store(const BufferSlice &buffer_slice, StorerT &storer) {
 | 
						|
  storer.store_string(buffer_slice);
 | 
						|
}
 | 
						|
 | 
						|
template <class ParserT>
 | 
						|
void parse(BufferSlice &buffer_slice, ParserT &parser) {
 | 
						|
  buffer_slice = parser.template fetch_string<BufferSlice>();
 | 
						|
}
 | 
						|
 | 
						|
class BufferWriter {
 | 
						|
 public:
 | 
						|
  BufferWriter() = default;
 | 
						|
  explicit BufferWriter(size_t size) : BufferWriter(BufferAllocator::create_writer(size)) {
 | 
						|
  }
 | 
						|
  BufferWriter(size_t size, size_t prepend, size_t append)
 | 
						|
      : BufferWriter(BufferAllocator::create_writer(size, prepend, append)) {
 | 
						|
  }
 | 
						|
  BufferWriter(Slice slice, size_t prepend, size_t append)
 | 
						|
      : BufferWriter(BufferAllocator::create_writer(slice.size(), prepend, append)) {
 | 
						|
    as_slice().copy_from(slice);
 | 
						|
  }
 | 
						|
  explicit BufferWriter(BufferWriterPtr buffer_ptr) : buffer_(std::move(buffer_ptr)) {
 | 
						|
  }
 | 
						|
 | 
						|
  BufferSlice as_buffer_slice() const {
 | 
						|
    return BufferSlice(BufferAllocator::create_reader(buffer_));
 | 
						|
  }
 | 
						|
  bool is_null() const {
 | 
						|
    return !buffer_;
 | 
						|
  }
 | 
						|
  bool empty() const {
 | 
						|
    return size() == 0;
 | 
						|
  }
 | 
						|
  size_t size() const {
 | 
						|
    if (is_null()) {
 | 
						|
      return 0;
 | 
						|
    }
 | 
						|
    return buffer_->end_.load(std::memory_order_relaxed) - buffer_->begin_;
 | 
						|
  }
 | 
						|
  MutableSlice as_slice() {
 | 
						|
    auto end = buffer_->end_.load(std::memory_order_relaxed);
 | 
						|
    return MutableSlice(buffer_->data_ + buffer_->begin_, buffer_->data_ + end);
 | 
						|
  }
 | 
						|
  Slice as_slice() const {
 | 
						|
    auto end = buffer_->end_.load(std::memory_order_relaxed);
 | 
						|
    return Slice(buffer_->data_ + buffer_->begin_, buffer_->data_ + end);
 | 
						|
  }
 | 
						|
 | 
						|
  MutableSlice prepare_prepend() {
 | 
						|
    if (is_null()) {
 | 
						|
      return MutableSlice();
 | 
						|
    }
 | 
						|
    CHECK(!buffer_->was_reader_);
 | 
						|
    return MutableSlice(buffer_->data_, buffer_->begin_);
 | 
						|
  }
 | 
						|
  MutableSlice prepare_append() {
 | 
						|
    if (is_null()) {
 | 
						|
      return MutableSlice();
 | 
						|
    }
 | 
						|
    auto end = buffer_->end_.load(std::memory_order_relaxed);
 | 
						|
    return MutableSlice(buffer_->data_ + end, buffer_->data_size_ - end);
 | 
						|
  }
 | 
						|
  void confirm_append(size_t size) {
 | 
						|
    if (is_null()) {
 | 
						|
      CHECK(size == 0);
 | 
						|
      return;
 | 
						|
    }
 | 
						|
    auto new_end = buffer_->end_.load(std::memory_order_relaxed) + size;
 | 
						|
    CHECK(new_end <= buffer_->data_size_);
 | 
						|
    buffer_->end_.store(new_end, std::memory_order_release);
 | 
						|
  }
 | 
						|
  void confirm_prepend(size_t size) {
 | 
						|
    if (is_null()) {
 | 
						|
      CHECK(size == 0);
 | 
						|
      return;
 | 
						|
    }
 | 
						|
    CHECK(buffer_->begin_ >= size);
 | 
						|
    buffer_->begin_ -= size;
 | 
						|
  }
 | 
						|
 | 
						|
 private:
 | 
						|
  BufferWriterPtr buffer_;
 | 
						|
};
 | 
						|
 | 
						|
struct ChainBufferNode {
 | 
						|
  friend struct DeleteWriterPtr;
 | 
						|
  struct DeleteWriterPtr {
 | 
						|
    void operator()(ChainBufferNode *ptr) {
 | 
						|
      ptr->has_writer_.store(false, std::memory_order_release);
 | 
						|
      dec_ref_cnt(ptr);
 | 
						|
    }
 | 
						|
  };
 | 
						|
  friend struct DeleteReaderPtr;
 | 
						|
  struct DeleteReaderPtr {
 | 
						|
    void operator()(ChainBufferNode *ptr) {
 | 
						|
      dec_ref_cnt(ptr);
 | 
						|
    }
 | 
						|
  };
 | 
						|
  using WriterPtr = std::unique_ptr<ChainBufferNode, DeleteWriterPtr>;
 | 
						|
  using ReaderPtr = std::unique_ptr<ChainBufferNode, DeleteReaderPtr>;
 | 
						|
 | 
						|
  static WriterPtr make_writer_ptr(ChainBufferNode *ptr) {
 | 
						|
    ptr->ref_cnt_.store(1, std::memory_order_relaxed);
 | 
						|
    ptr->has_writer_.store(true, std::memory_order_relaxed);
 | 
						|
    return WriterPtr(ptr);
 | 
						|
  }
 | 
						|
  static ReaderPtr make_reader_ptr(ChainBufferNode *ptr) {
 | 
						|
    ptr->ref_cnt_.fetch_add(1, std::memory_order_acq_rel);
 | 
						|
    return ReaderPtr(ptr);
 | 
						|
  }
 | 
						|
 | 
						|
  bool has_writer() {
 | 
						|
    return has_writer_.load(std::memory_order_acquire);
 | 
						|
  }
 | 
						|
 | 
						|
  bool unique() {
 | 
						|
    return ref_cnt_.load(std::memory_order_acquire) == 1;
 | 
						|
  }
 | 
						|
 | 
						|
  ChainBufferNode(BufferSlice slice, bool sync_flag) : slice_(std::move(slice)), sync_flag_(sync_flag) {
 | 
						|
  }
 | 
						|
 | 
						|
  // reader
 | 
						|
  // There are two options
 | 
						|
  // 1. Fixed slice of Buffer
 | 
						|
  // 2. Slice with non-fixed right end
 | 
						|
  // In each case slice_ is const. Reader should read it and use sync_with_writer on its own copy.
 | 
						|
  const BufferSlice slice_;
 | 
						|
  const bool sync_flag_{false};  // should we call slice_.sync_with_writer or not.
 | 
						|
 | 
						|
  // writer
 | 
						|
  ReaderPtr next_{nullptr};
 | 
						|
 | 
						|
 private:
 | 
						|
  std::atomic<int> ref_cnt_{0};
 | 
						|
  std::atomic<bool> has_writer_{false};
 | 
						|
 | 
						|
  static void clear_nonrecursive(ReaderPtr ptr) {
 | 
						|
    while (ptr && ptr->unique()) {
 | 
						|
      ptr = std::move(ptr->next_);
 | 
						|
    }
 | 
						|
  }
 | 
						|
  static void dec_ref_cnt(ChainBufferNode *ptr) {
 | 
						|
    int left = --ptr->ref_cnt_;
 | 
						|
    if (left == 0) {
 | 
						|
      clear_nonrecursive(std::move(ptr->next_));
 | 
						|
      // TODO(refact): move memory management into allocator (?)
 | 
						|
      delete ptr;
 | 
						|
    }
 | 
						|
  }
 | 
						|
};
 | 
						|
 | 
						|
using ChainBufferNodeWriterPtr = ChainBufferNode::WriterPtr;
 | 
						|
using ChainBufferNodeReaderPtr = ChainBufferNode::ReaderPtr;
 | 
						|
 | 
						|
class ChainBufferNodeAllocator {
 | 
						|
 public:
 | 
						|
  static ChainBufferNodeWriterPtr create(BufferSlice slice, bool sync_flag) {
 | 
						|
    auto *ptr = new ChainBufferNode(std::move(slice), sync_flag);
 | 
						|
    return ChainBufferNode::make_writer_ptr(ptr);
 | 
						|
  }
 | 
						|
  static ChainBufferNodeReaderPtr clone(const ChainBufferNodeReaderPtr &ptr) {
 | 
						|
    if (!ptr) {
 | 
						|
      return ChainBufferNodeReaderPtr();
 | 
						|
    }
 | 
						|
    return ChainBufferNode::make_reader_ptr(ptr.get());
 | 
						|
  }
 | 
						|
  static ChainBufferNodeReaderPtr clone(ChainBufferNodeWriterPtr &ptr) {
 | 
						|
    if (!ptr) {
 | 
						|
      return ChainBufferNodeReaderPtr();
 | 
						|
    }
 | 
						|
    return ChainBufferNode::make_reader_ptr(ptr.get());
 | 
						|
  }
 | 
						|
};
 | 
						|
 | 
						|
class ChainBufferIterator {
 | 
						|
 public:
 | 
						|
  ChainBufferIterator() = default;
 | 
						|
  explicit ChainBufferIterator(ChainBufferNodeReaderPtr head) : head_(std::move(head)) {
 | 
						|
    load_head();
 | 
						|
  }
 | 
						|
  ChainBufferIterator clone() const {
 | 
						|
    return ChainBufferIterator(ChainBufferNodeAllocator::clone(head_), reader_.clone(), need_sync_, offset_);
 | 
						|
  }
 | 
						|
 | 
						|
  size_t offset() const {
 | 
						|
    return offset_;
 | 
						|
  }
 | 
						|
 | 
						|
  void clear() {
 | 
						|
    *this = ChainBufferIterator();
 | 
						|
  }
 | 
						|
 | 
						|
  Slice prepare_read() {
 | 
						|
    if (!head_) {
 | 
						|
      return Slice();
 | 
						|
    }
 | 
						|
    while (true) {
 | 
						|
      auto res = reader_.prepare_read();
 | 
						|
      if (!res.empty()) {
 | 
						|
        return res;
 | 
						|
      }
 | 
						|
      auto has_writer = head_->has_writer();
 | 
						|
      if (need_sync_) {
 | 
						|
        reader_.sync_with_writer();
 | 
						|
        res = reader_.prepare_read();
 | 
						|
        if (!res.empty()) {
 | 
						|
          return res;
 | 
						|
        }
 | 
						|
      }
 | 
						|
      if (has_writer) {
 | 
						|
        return Slice();
 | 
						|
      }
 | 
						|
      head_ = ChainBufferNodeAllocator::clone(head_->next_);
 | 
						|
      if (!head_) {
 | 
						|
        return Slice();
 | 
						|
      }
 | 
						|
      load_head();
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  // returns only head
 | 
						|
  BufferSlice read_as_buffer_slice(size_t limit) {
 | 
						|
    prepare_read();
 | 
						|
    auto res = reader_.clone();
 | 
						|
    res.truncate(limit);
 | 
						|
    confirm_read(res.size());
 | 
						|
    return res;
 | 
						|
  }
 | 
						|
 | 
						|
  const BufferSlice &head() const {
 | 
						|
    return reader_;
 | 
						|
  }
 | 
						|
 | 
						|
  void confirm_read(size_t size) {
 | 
						|
    offset_ += size;
 | 
						|
    reader_.confirm_read(size);
 | 
						|
  }
 | 
						|
 | 
						|
  void advance_till_end() {
 | 
						|
    while (true) {
 | 
						|
      auto ready = prepare_read();
 | 
						|
      if (ready.empty()) {
 | 
						|
        break;
 | 
						|
      }
 | 
						|
      confirm_read(ready.size());
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  size_t advance(size_t offset, MutableSlice dest = MutableSlice()) {
 | 
						|
    size_t skipped = 0;
 | 
						|
    while (offset != 0) {
 | 
						|
      auto ready = prepare_read();
 | 
						|
      if (ready.empty()) {
 | 
						|
        break;
 | 
						|
      }
 | 
						|
 | 
						|
      // read no more than offset
 | 
						|
      ready.truncate(offset);
 | 
						|
      offset -= ready.size();
 | 
						|
      skipped += ready.size();
 | 
						|
 | 
						|
      // copy to dest if possible
 | 
						|
      auto to_dest_size = min(ready.size(), dest.size());
 | 
						|
      if (to_dest_size != 0) {
 | 
						|
        dest.copy_from(ready.substr(0, to_dest_size));
 | 
						|
        dest.remove_prefix(to_dest_size);
 | 
						|
      }
 | 
						|
 | 
						|
      confirm_read(ready.size());
 | 
						|
    }
 | 
						|
    return skipped;
 | 
						|
  }
 | 
						|
 | 
						|
 private:
 | 
						|
  ChainBufferNodeReaderPtr head_;
 | 
						|
  BufferSlice reader_;      // copy of head_->slice_
 | 
						|
  bool need_sync_ = false;  // copy of head_->sync_flag_
 | 
						|
  size_t offset_ = 0;       // position in the union of all nodes
 | 
						|
 | 
						|
  ChainBufferIterator(ChainBufferNodeReaderPtr head, BufferSlice reader, bool need_sync, size_t offset)
 | 
						|
      : head_(std::move(head)), reader_(std::move(reader)), need_sync_(need_sync), offset_(offset) {
 | 
						|
  }
 | 
						|
  void load_head() {
 | 
						|
    reader_ = head_->slice_.clone();
 | 
						|
    need_sync_ = head_->sync_flag_;
 | 
						|
  }
 | 
						|
};
 | 
						|
 | 
						|
class ChainBufferReader {
 | 
						|
 public:
 | 
						|
  ChainBufferReader() = default;
 | 
						|
  explicit ChainBufferReader(ChainBufferNodeReaderPtr head)
 | 
						|
      : begin_(ChainBufferNodeAllocator::clone(head)), end_(std::move(head)) {
 | 
						|
    end_.advance_till_end();
 | 
						|
  }
 | 
						|
  ChainBufferReader(ChainBufferIterator begin, ChainBufferIterator end, bool sync_flag)
 | 
						|
      : begin_(std::move(begin)), end_(std::move(end)), sync_flag_(sync_flag) {
 | 
						|
  }
 | 
						|
  ChainBufferReader(ChainBufferNodeReaderPtr head, size_t size)
 | 
						|
      : begin_(ChainBufferNodeAllocator::clone(head)), end_(std::move(head)) {
 | 
						|
    auto advanced = end_.advance(size);
 | 
						|
    CHECK(advanced == size);
 | 
						|
  }
 | 
						|
  ChainBufferReader(ChainBufferReader &&) = default;
 | 
						|
  ChainBufferReader &operator=(ChainBufferReader &&) = default;
 | 
						|
  ChainBufferReader(const ChainBufferReader &) = delete;
 | 
						|
  ChainBufferReader &operator=(const ChainBufferReader &) = delete;
 | 
						|
  ~ChainBufferReader() = default;
 | 
						|
 | 
						|
  ChainBufferReader clone() {
 | 
						|
    return ChainBufferReader(begin_.clone(), end_.clone(), sync_flag_);
 | 
						|
  }
 | 
						|
 | 
						|
  Slice prepare_read() {
 | 
						|
    auto res = begin_.prepare_read();
 | 
						|
    res.truncate(size());
 | 
						|
    return res;
 | 
						|
  }
 | 
						|
 | 
						|
  void confirm_read(size_t size) {
 | 
						|
    CHECK(size <= this->size());
 | 
						|
    begin_.confirm_read(size);
 | 
						|
  }
 | 
						|
 | 
						|
  size_t advance(size_t offset, MutableSlice dest = MutableSlice()) {
 | 
						|
    CHECK(offset <= size());
 | 
						|
    return begin_.advance(offset, dest);
 | 
						|
  }
 | 
						|
 | 
						|
  size_t size() const {
 | 
						|
    return end_.offset() - begin_.offset();
 | 
						|
  }
 | 
						|
  bool empty() const {
 | 
						|
    return size() == 0;
 | 
						|
  }
 | 
						|
 | 
						|
  void sync_with_writer() {
 | 
						|
    if (sync_flag_) {
 | 
						|
      end_.advance_till_end();
 | 
						|
    }
 | 
						|
  }
 | 
						|
  void advance_end(size_t size) {
 | 
						|
    end_.advance(size);
 | 
						|
  }
 | 
						|
  const ChainBufferIterator &begin() {
 | 
						|
    return begin_;
 | 
						|
  }
 | 
						|
  const ChainBufferIterator &end() {
 | 
						|
    return end_;
 | 
						|
  }
 | 
						|
 | 
						|
  // Return [begin_, tail.begin_)
 | 
						|
  // *this = tail
 | 
						|
  ChainBufferReader cut_head(ChainBufferIterator pos) TD_WARN_UNUSED_RESULT {
 | 
						|
    auto tmp = begin_.clone();
 | 
						|
    begin_ = pos.clone();
 | 
						|
    return ChainBufferReader(std::move(tmp), std::move(pos), false);
 | 
						|
  }
 | 
						|
 | 
						|
  ChainBufferReader cut_head(size_t offset) TD_WARN_UNUSED_RESULT {
 | 
						|
    CHECK(offset <= size());
 | 
						|
    auto it = begin_.clone();
 | 
						|
    it.advance(offset);
 | 
						|
    return cut_head(std::move(it));
 | 
						|
  }
 | 
						|
 | 
						|
  BufferSlice move_as_buffer_slice() {
 | 
						|
    BufferSlice res;
 | 
						|
    if (begin_.head().size() >= size()) {
 | 
						|
      res = begin_.read_as_buffer_slice(size());
 | 
						|
    } else {
 | 
						|
      auto save_size = size();
 | 
						|
      res = BufferSlice{save_size};
 | 
						|
      advance(save_size, res.as_slice());
 | 
						|
    }
 | 
						|
    *this = ChainBufferReader();
 | 
						|
    return res;
 | 
						|
  }
 | 
						|
 | 
						|
  BufferSlice read_as_buffer_slice(size_t limit = std::numeric_limits<size_t>::max()) {
 | 
						|
    return begin_.read_as_buffer_slice(min(limit, size()));
 | 
						|
  }
 | 
						|
 | 
						|
 private:
 | 
						|
  ChainBufferIterator begin_;  // use it for prepare_read. Fix result with size()
 | 
						|
  ChainBufferIterator end_;    // keep end as far as we can. use it for size()
 | 
						|
  bool sync_flag_ = true;      // auto sync of end_
 | 
						|
 | 
						|
  // 1. We have fixed size. Than end_ is useless.
 | 
						|
  // 2. No fixed size. One has to sync end_ with end_.advance_till_end() in order to calculate size.
 | 
						|
};
 | 
						|
 | 
						|
class ChainBufferWriter {
 | 
						|
 public:
 | 
						|
  ChainBufferWriter() {
 | 
						|
    init();
 | 
						|
  }
 | 
						|
 | 
						|
  void init(size_t size = 0) {
 | 
						|
    writer_ = BufferWriter(size);
 | 
						|
    tail_ = ChainBufferNodeAllocator::create(writer_.as_buffer_slice(), true);
 | 
						|
    head_ = ChainBufferNodeAllocator::clone(tail_);
 | 
						|
  }
 | 
						|
 | 
						|
  MutableSlice prepare_append(size_t hint = 0) {
 | 
						|
    CHECK(!empty());
 | 
						|
    auto res = prepare_append_inplace();
 | 
						|
    if (res.empty()) {
 | 
						|
      return prepare_append_alloc(hint);
 | 
						|
    }
 | 
						|
    return res;
 | 
						|
  }
 | 
						|
  MutableSlice prepare_append_at_least(size_t size) {
 | 
						|
    CHECK(!empty());
 | 
						|
    auto res = prepare_append_inplace();
 | 
						|
    if (res.size() < size) {
 | 
						|
      return prepare_append_alloc(size);
 | 
						|
    }
 | 
						|
    return res;
 | 
						|
  }
 | 
						|
  MutableSlice prepare_append_inplace() {
 | 
						|
    CHECK(!empty());
 | 
						|
    return writer_.prepare_append();
 | 
						|
  }
 | 
						|
  MutableSlice prepare_append_alloc(size_t hint = 0) {
 | 
						|
    CHECK(!empty());
 | 
						|
    if (hint < (1 << 10)) {
 | 
						|
      hint = 1 << 12;
 | 
						|
    }
 | 
						|
    BufferWriter new_writer(hint);
 | 
						|
    auto new_tail = ChainBufferNodeAllocator::create(new_writer.as_buffer_slice(), true);
 | 
						|
    tail_->next_ = ChainBufferNodeAllocator::clone(new_tail);
 | 
						|
    writer_ = std::move(new_writer);
 | 
						|
    tail_ = std::move(new_tail);  // release tail_
 | 
						|
    return writer_.prepare_append();
 | 
						|
  }
 | 
						|
  void confirm_append(size_t size) {
 | 
						|
    CHECK(!empty());
 | 
						|
    writer_.confirm_append(size);
 | 
						|
  }
 | 
						|
 | 
						|
  void append(Slice slice, size_t hint = 0) {
 | 
						|
    while (!slice.empty()) {
 | 
						|
      auto ready = prepare_append(td::max(slice.size(), hint));
 | 
						|
      auto shift = min(ready.size(), slice.size());
 | 
						|
      ready.copy_from(slice.substr(0, shift));
 | 
						|
      confirm_append(shift);
 | 
						|
      slice.remove_prefix(shift);
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  void append(BufferSlice slice) {
 | 
						|
    auto ready = prepare_append_inplace();
 | 
						|
    // TODO(perf): we have to store some stats in ChainBufferWriter
 | 
						|
    // for better append logic
 | 
						|
    if (slice.size() < (1 << 8) || ready.size() >= slice.size()) {
 | 
						|
      return append(slice.as_slice());
 | 
						|
    }
 | 
						|
 | 
						|
    auto new_tail = ChainBufferNodeAllocator::create(std::move(slice), false);
 | 
						|
    tail_->next_ = ChainBufferNodeAllocator::clone(new_tail);
 | 
						|
    writer_ = BufferWriter();
 | 
						|
    tail_ = std::move(new_tail);  // release tail_
 | 
						|
  }
 | 
						|
 | 
						|
  void append(ChainBufferReader &&reader) {
 | 
						|
    while (!reader.empty()) {
 | 
						|
      append(reader.read_as_buffer_slice());
 | 
						|
    }
 | 
						|
  }
 | 
						|
  void append(ChainBufferReader &reader) {
 | 
						|
    while (!reader.empty()) {
 | 
						|
      append(reader.read_as_buffer_slice());
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  ChainBufferReader extract_reader() {
 | 
						|
    CHECK(head_);
 | 
						|
    return ChainBufferReader(std::move(head_));
 | 
						|
  }
 | 
						|
 | 
						|
 private:
 | 
						|
  bool empty() const {
 | 
						|
    return !tail_;
 | 
						|
  }
 | 
						|
 | 
						|
  ChainBufferNodeReaderPtr head_;
 | 
						|
  ChainBufferNodeWriterPtr tail_;
 | 
						|
  BufferWriter writer_;
 | 
						|
};
 | 
						|
 | 
						|
class BufferBuilder {
 | 
						|
 public:
 | 
						|
  BufferBuilder() = default;
 | 
						|
  BufferBuilder(Slice slice, size_t prepend_size, size_t append_size)
 | 
						|
      : buffer_writer_(slice, prepend_size, append_size) {
 | 
						|
  }
 | 
						|
  explicit BufferBuilder(BufferWriter &&buffer_writer) : buffer_writer_(std::move(buffer_writer)) {
 | 
						|
  }
 | 
						|
 | 
						|
  void append(BufferSlice slice);
 | 
						|
  void append(Slice slice);
 | 
						|
 | 
						|
  void prepend(BufferSlice slice);
 | 
						|
  void prepend(Slice slice);
 | 
						|
 | 
						|
  template <class F>
 | 
						|
  void for_each(F &&f) const & {
 | 
						|
    for (auto i = to_prepend_.size(); i > 0; i--) {
 | 
						|
      f(to_prepend_[i - 1].as_slice());
 | 
						|
    }
 | 
						|
    if (!buffer_writer_.empty()) {
 | 
						|
      f(buffer_writer_.as_slice());
 | 
						|
    }
 | 
						|
    for (auto &slice : to_append_) {
 | 
						|
      f(slice.as_slice());
 | 
						|
    }
 | 
						|
  }
 | 
						|
  template <class F>
 | 
						|
  void for_each(F &&f) && {
 | 
						|
    for (auto i = to_prepend_.size(); i > 0; i--) {
 | 
						|
      f(std::move(to_prepend_[i - 1]));
 | 
						|
    }
 | 
						|
    if (!buffer_writer_.empty()) {
 | 
						|
      f(buffer_writer_.as_buffer_slice());
 | 
						|
    }
 | 
						|
    for (auto &slice : to_append_) {
 | 
						|
      f(std::move(slice));
 | 
						|
    }
 | 
						|
  }
 | 
						|
  size_t size() const;
 | 
						|
 | 
						|
  BufferSlice extract();
 | 
						|
 | 
						|
 private:
 | 
						|
  BufferWriter buffer_writer_;
 | 
						|
  std::vector<BufferSlice> to_append_;
 | 
						|
  std::vector<BufferSlice> to_prepend_;
 | 
						|
 | 
						|
  bool append_inplace(Slice slice);
 | 
						|
  void append_slow(BufferSlice slice);
 | 
						|
  bool prepend_inplace(Slice slice);
 | 
						|
  void prepend_slow(BufferSlice slice);
 | 
						|
};
 | 
						|
 | 
						|
inline Slice as_slice(const BufferSlice &value) {
 | 
						|
  return value.as_slice();
 | 
						|
}
 | 
						|
inline MutableSlice as_slice(BufferSlice &value) {
 | 
						|
  return value.as_slice();
 | 
						|
}
 | 
						|
inline MutableSlice as_mutable_slice(BufferSlice &value) {
 | 
						|
  return value.as_slice();
 | 
						|
}
 | 
						|
 | 
						|
}  // namespace td
 |