text: /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
  /*************************************************************************
   *
   * Copyright (c) 2021 Kohei Yoshida
   *
   * Permission is hereby granted, free of charge, to any person
   * obtaining a copy of this software and associated documentation
   * files (the "Software"), to deal in the Software without
   * restriction, including without limitation the rights to use,
   * copy, modify, merge, publish, distribute, sublicense, and/or sell
   * copies of the Software, and to permit persons to whom the
   * Software is furnished to do so, subject to the following
   * conditions:
   *
   * The above copyright notice and this permission notice shall be
   * included in all copies or substantial portions of the Software.
   *
   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
   * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
   * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
   * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
   * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
   * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
   * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
   * OTHER DEALINGS IN THE SOFTWARE.
   *
   ************************************************************************/
  
  #ifndef INCLUDED_MDDS_MULTI_TYPE_VECTOR_DIR_UTIL_HPP
  #define INCLUDED_MDDS_MULTI_TYPE_VECTOR_DIR_UTIL_HPP
  
  #include "./types.hpp"
  
  #include <sstream>
  
  namespace mdds {
  
  namespace mtv {
  
  /**
   * Empty event function handler structure, used when no custom function
   * handler is specified.
   */
  struct empty_event_func
  {
      /**
       * Callback function for element block acquisition events.  This gets called
       * whenever the container acquires a new element block either as a result of
       * a new element block creation or a transfer of an existing element block
       * from another container.
       *
       * @param block pointer to the acquired element block instance.
       */
      void element_block_acquired(const base_element_block* block) {}
  
      /**
       * Callback function for element block release events.  This gets called
       * whenever the container releases an existing element block either because
       * the block is about to be deleted or to be transferred to another
       * container.
       *
       * @param block pointer to the element block instance being released.
       */
      void element_block_released(const base_element_block* block) {}
  };
  
  /**
   * Default trait to be used when no custom trait is specified.
   */
  struct default_trait
  {
      /**
       * Class or struct type that contains callback functions for element block
       * events as its member functions.
       */
      using event_func = empty_event_func;
  
      /**
       * Static value specifying the loop-unrolling factor to use for the block
       * position adjustment function.  This must be a const expression.
       */
      static constexpr lu_factor_t loop_unrolling = lu_factor_t::lu16;
  };
  
  } // namespace mtv
  
  namespace detail { namespace mtv {
  
  inline void throw_block_position_not_found(
      const char* method_sig, int line, size_t pos, size_t block_size, size_t container_size)
  {
      std::ostringstream os;
      os << method_sig << "#" << line << ": block position not found! (logical pos="
          << pos << ", block size=" << block_size << ", logical size=" << container_size << ")";
      throw std::out_of_range(os.str());
  }
  
  /**
   * Given a pair of iterators comprising the input value sequence, and a
   * desired logical insertion position, calculate the position of the last
   * input value. Also check if the input value sequence is empty.
   *
   * @exception std::out_of_range if the end position is greater than the last
   *               allowed position of the destination storage.
   *
   * @param it_begin iterator pointing to the first element of the input value
   *                 sequence.
   * @param it_end iterator point to the position past the last element of the
   *               input value sequence.
   * @param pos desired insertion position.
   * @param total_size total logical size of the destination storage.
   *
   * @return position of the last input value (first) and a flag on whether or
   *         not the input value sequence is empty (second).
   */
  template<typename _T, typename _SizeT>
  std::pair<_SizeT, bool> calc_input_end_position(
      const _T& it_begin, const _T& it_end, _SizeT pos, _SizeT total_size)
  {
      using ret_type = std::pair<_SizeT, bool>;
  
      _SizeT length = std::distance(it_begin, it_end);
      if (!length)
          // empty data array.  nothing to do.
          return ret_type(0, false);
  
      _SizeT end_pos = pos + length - 1;
      if (end_pos >= total_size)
          throw std::out_of_range("Input data sequence is too long.");
  
      return ret_type(end_pos, true);
  }
  
  template<typename T>
  T advance_position(const T& pos, int steps)
  {
      T ret = pos;
  
      if (steps > 0)
      {
          while (steps > 0)
          {
              if (ret.second + steps < ret.first->size)
              {
                  // element is still in the same block.
                  ret.second += steps;
                  break;
              }
              else
              {
                  steps -= static_cast<int>(ret.first->size - ret.second);
                  ++ret.first;
                  ret.second = 0;
              }
          }
      }
      else
      {
          while (steps < 0)
          {
              if (static_cast<int>(ret.second) >= -steps)
              {
                  ret.second += steps;
                  break;
              }
              else
              {
                  steps += static_cast<int>(ret.second + 1);
                  --ret.first;
                  ret.second = ret.first->size - 1;
              }
          }
      }
  
      return ret;
  }
  
  template<typename _Blk>
  inline typename _Blk::value_type get_block_element_at(const mdds::mtv::base_element_block& data, size_t offset)
  {
      return _Blk::at(data, offset);
  }
  
  #ifndef MDDS_MULTI_TYPE_VECTOR_USE_DEQUE
  
  template<>
  inline bool get_block_element_at<mdds::mtv::boolean_element_block>(const mdds::mtv::base_element_block& data, size_t offset)
  {
      auto it = mdds::mtv::boolean_element_block::cbegin(data);
      std::advance(it, offset);
      return *it;
  }
  
  #endif
  
  }} // namespace detail::mtv
  
  }
  
  #endif
  
  /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
  
  