1 // 2 // Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2021 3 // 4 // Distributed under the Boost Software License, Version 1.0. (See accompanying 5 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 6 // 7 #pragma once 8 9 #include "td/utils/common.h" 10 #include "td/utils/logging.h" 11 #include "td/utils/Slice.h" 12 13 #include <atomic> 14 #include <cstdio> 15 #include <cstring> 16 17 namespace td { 18 19 template <int buffer_size = 32 * (1 << 10)> 20 class MemoryLog final : public LogInterface { 21 static constexpr size_t MAX_OUTPUT_SIZE = buffer_size / 16 < (8 << 10) ? buffer_size / 16 : (8 << 10); 22 23 static_assert((buffer_size & (buffer_size - 1)) == 0, "Buffer size must be power of 2"); 24 static_assert(buffer_size >= (8 << 10), "Too small buffer size"); 25 26 public: MemoryLog()27 MemoryLog() { 28 std::memset(buffer_, ' ', sizeof(buffer_)); 29 } 30 get_buffer()31 Slice get_buffer() const { 32 return Slice(buffer_, sizeof(buffer_)); 33 } 34 get_pos()35 size_t get_pos() const { 36 return pos_ & (buffer_size - 1); 37 } 38 39 private: do_append(int log_level,CSlice new_slice)40 void do_append(int log_level, CSlice new_slice) final { 41 Slice slice = new_slice; 42 slice.truncate(MAX_OUTPUT_SIZE); 43 while (!slice.empty() && slice.back() == '\n') { 44 slice.remove_suffix(1); 45 } 46 size_t slice_size = slice.size(); 47 CHECK(slice_size * 3 < buffer_size); 48 size_t pad_size = ((slice_size + 15) & ~15) - slice_size; 49 constexpr size_t MAGIC_SIZE = 16; 50 auto total_size = static_cast<uint32>(slice_size + pad_size + MAGIC_SIZE); 51 auto real_pos = pos_.fetch_add(total_size, std::memory_order_relaxed); 52 CHECK((total_size & 15) == 0); 53 54 uint32 start_pos = real_pos & (buffer_size - 1); 55 uint32 end_pos = start_pos + total_size; 56 if (likely(end_pos <= buffer_size)) { 57 std::memcpy(&buffer_[start_pos + MAGIC_SIZE], slice.data(), slice_size); 58 std::memcpy(&buffer_[start_pos + MAGIC_SIZE + slice_size], " ", pad_size); 59 } else { 60 size_t first = buffer_size - start_pos - MAGIC_SIZE; 61 size_t second = slice_size - first; 62 std::memcpy(&buffer_[start_pos + MAGIC_SIZE], slice.data(), first); 63 std::memcpy(&buffer_[0], slice.data() + first, second); 64 std::memcpy(&buffer_[second], " ", pad_size); 65 } 66 67 CHECK((start_pos & 15) == 0); 68 CHECK(start_pos <= buffer_size - MAGIC_SIZE); 69 buffer_[start_pos] = '\n'; 70 size_t printed = std::snprintf(&buffer_[start_pos + 1], MAGIC_SIZE - 1, "LOG:%08x: ", real_pos); 71 CHECK(printed == MAGIC_SIZE - 2); 72 buffer_[start_pos + MAGIC_SIZE - 1] = ' '; 73 } 74 75 char buffer_[buffer_size]; 76 std::atomic<uint32> pos_{0}; 77 }; 78 79 } // namespace td 80