text: //
  // Copyright(c) 2015 Gabi Melman.
  // Distributed under the MIT License (http://opensource.org/licenses/MIT)
  //
  
  #pragma once
  
  //
  // Support for logging binary data as hex
  // format flags:
  // {:X} - print in uppercase.
  // {:s} - don't separate each byte with space.
  // {:p} - don't print the position on each line start.
  // {:n} - don't split the output to lines.
  
  //
  // Examples:
  //
  // std::vector<char> v(200, 0x0b);
  // logger->info("Some buffer {}", spdlog::to_hex(v));
  // char buf[128];
  // logger->info("Some buffer {:X}", spdlog::to_hex(std::begin(buf), std::end(buf)));
  
  namespace spdlog {
  namespace details {
  
  template<typename It>
  class bytes_range
  {
  public:
      bytes_range(It range_begin, It range_end)
          : begin_(range_begin)
          , end_(range_end)
      {
      }
  
      It begin() const
      {
          return begin_;
      }
      It end() const
      {
          return end_;
      }
  
  private:
      It begin_, end_;
  };
  } // namespace details
  
  // create a bytes_range that wraps the given container
  template<typename Container>
  inline details::bytes_range<typename Container::const_iterator> to_hex(const Container &container)
  {
      static_assert(sizeof(typename Container::value_type) == 1, "sizeof(Container::value_type) != 1");
      using Iter = typename Container::const_iterator;
      return details::bytes_range<Iter>(std::begin(container), std::end(container));
  }
  
  // create bytes_range from ranges
  template<typename It>
  inline details::bytes_range<It> to_hex(const It range_begin, const It range_end)
  {
      return details::bytes_range<It>(range_begin, range_end);
  }
  
  } // namespace spdlog
  
  namespace fmt {
  
  template<typename T>
  struct formatter<spdlog::details::bytes_range<T>>
  {
      const std::size_t line_size = 100;
      const char delimiter = ' ';
  
      bool put_newlines = true;
      bool put_delimiters = true;
      bool use_uppercase = false;
      bool put_positions = true; // position on start of each line
  
      // parse the format string flags
      template<typename ParseContext>
      auto parse(ParseContext &ctx) -> decltype(ctx.begin())
      {
          auto it = ctx.begin();
          while (*it && *it != '}')
          {
              switch (*it)
              {
              case 'X':
                  use_uppercase = true;
                  break;
              case 's':
                  put_delimiters = false;
                  break;
              case 'p':
                  put_positions = false;
                  break;
              case 'n':
                  put_newlines = false;
                  break;
              }
  
              ++it;
          }
          return it;
      }
  
      // format the given bytes range as hex
      template<typename FormatContext, typename Container>
      auto format(const spdlog::details::bytes_range<Container> &the_range, FormatContext &ctx) -> decltype(ctx.out())
      {
          SPDLOG_CONSTEXPR const char *hex_upper = "0123456789ABCDEF";
          SPDLOG_CONSTEXPR const char *hex_lower = "0123456789abcdef";
          const char *hex_chars = use_uppercase ? hex_upper : hex_lower;
  
          std::size_t pos = 0;
          std::size_t column = line_size;
          auto inserter = ctx.begin();
  
          for (auto &item : the_range)
          {
              auto ch = static_cast<unsigned char>(item);
              pos++;
  
              if (put_newlines && column >= line_size)
              {
                  column = put_newline(inserter, pos);
  
                  // put first byte without delimiter in front of it
                  *inserter++ = hex_chars[(ch >> 4) & 0x0f];
                  *inserter++ = hex_chars[ch & 0x0f];
                  column += 2;
                  continue;
              }
  
              if (put_delimiters)
              {
                  *inserter++ = delimiter;
                  ++column;
              }
  
              *inserter++ = hex_chars[(ch >> 4) & 0x0f];
              *inserter++ = hex_chars[ch & 0x0f];
              column += 2;
          }
          return inserter;
      }
  
      // put newline(and position header)
      // return the next column
      template<typename It>
      std::size_t put_newline(It inserter, std::size_t pos)
      {
  #ifdef _WIN32
          *inserter++ = '\r';
  #endif
          *inserter++ = '\n';
  
          if (put_positions)
          {
              fmt::format_to(inserter, "{:<04X}: ", pos - 1);
              return 7;
          }
          else
          {
              return 1;
          }
      }
  };
  } // namespace fmt
  