text: /**
   *   Copyright 2016, Quickstep Research Group, Computer Sciences Department,
   *     University of Wisconsin—Madison.
   *
   *   Licensed under the Apache License, Version 2.0 (the "License");
   *   you may not use this file except in compliance with the License.
   *   You may obtain a copy of the License at
   *
   *       http://www.apache.org/licenses/LICENSE-2.0
   *
   *   Unless required by applicable law or agreed to in writing, software
   *   distributed under the License is distributed on an "AS IS" BASIS,
   *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   *   See the License for the specific language governing permissions and
   *   limitations under the License.
   **/
  
  #ifndef QUICKSTEP_CATALOG_INDEX_SCHEME_HPP_
  #define QUICKSTEP_CATALOG_INDEX_SCHEME_HPP_
  
  #include <algorithm>
  #include <cstddef>
  #include <string>
  #include <unordered_map>
  #include <vector>
  
  #include "catalog/Catalog.pb.h"
  #include "storage/StorageBlockLayout.pb.h"
  #include "utility/Macros.hpp"
  
  #include "glog/logging.h"
  
  namespace quickstep {
  
  /** \addtogroup Catalog
   *  @{
   */
  
  /**
   * @brief The Index Scheme class which stores the information about
   *        various indicies defined for a particular relation.
   **/
  class IndexScheme {
   public:
    typedef std::unordered_map<std::string, IndexSubBlockDescription>::const_iterator const_iterator;
  
    /**
     * @brief Constructor.
     **/
    IndexScheme() {
    }
  
    /**
     * @brief Reconstruct an Index Scheme from its serialized
     *        Protocol Buffer form.
     *
     * @param proto The Protocol Buffer serialization of a Index Scheme,
     *              previously produced by getProto().
     * @return The deserialied index scheme object.
     **/
    static IndexScheme* ReconstructFromProto(const serialization::IndexScheme &proto);
  
    /**
     * @brief Check whether a serialization::IndexScheme is fully-formed and
     *        all parts are valid.
     *
     * @param proto A serialized Protocol Buffer representation of a
     *              IndexScheme, originally generated by getProto().
     * @return Whether proto is fully-formed and valid.
     **/
    static bool ProtoIsValid(const serialization::IndexScheme &proto);
  
    /**
     * @brief Serialize the Index Scheme as Protocol Buffer.
     *
     * @return The Protocol Buffer representation of Index Scheme.
     **/
    serialization::IndexScheme getProto() const;
  
    /**
     * @brief Get the number of indices for the relation.
     *
     * @return The number of indices defined for the relation.
     **/
    inline std::size_t getNumIndices() const {
      return index_map_.size();
    }
  
    /**
     * @brief Check whether an index with the given exists or not.
     *
     * @param index_name Name of the index to be checked.
     * @return Whether the index exists or not.
     **/
    bool hasIndexWithName(const std::string &index_name) const {
      return index_map_.find(index_name) != index_map_.end();
    }
  
    /**
     * @brief Check whether an index with the given description
     *        containing the same attribute id and index type
     *        exists or not in the index map.
     *
     * @param index_descripton Index Description to check against.
     * @return Whether a similar index description was already defined or not.
     **/
    bool hasIndexWithDescription(const IndexSubBlockDescription &index_description_checked) const {
      // Iterate through every index description corresponding to each key in the index map.
      for (auto cit = index_map_.cbegin(); cit != index_map_.cend(); ++cit) {
        const IndexSubBlockDescription &index_description_expected = cit->second;
        // Check if the stored description matches the given description.
        if (areIndexDescriptionsSame(index_description_expected, index_description_checked)) {
          return true;
        }
      }
      return false;
    }
  
    /**
     * @brief Check whether two index descriptions are same or not.
     *        Two index descriptions are same if they have any matching
     *        attributes ids and same index type.
     *
     * @param description_expected First index description.
     * @param description_checked Second index description.
     * @return Whether the two index_descriptions are similar or not.
     **/
    bool areIndexDescriptionsSame(const IndexSubBlockDescription &description_expected,
                                  const IndexSubBlockDescription &description_checked) const {
      if (description_expected.sub_block_type() != description_checked.sub_block_type()) {
        return false;
      }
  
      // Serialize and sort the two protobuf index descriptions and compare.
      std::string serialized_description_expected, serialized_description_checked;
      description_expected.SerializeToString(&serialized_description_expected);
      description_checked.SerializeToString(&serialized_description_checked);
      std::sort(serialized_description_expected.begin(), serialized_description_expected.end());
      std::sort(serialized_description_checked.begin(), serialized_description_checked.end());
  
      return (serialized_description_expected.compare(serialized_description_checked) == 0);
    }
  
    /**
     * @brief Get an iterator at beginning of the index map.
     *
     * @return An iterator at beginning of the index map.
     **/
    inline const_iterator begin() const {
      return index_map_.begin();
    }
  
    /**
     * @brief Get an iterator at one-past-the-end of the index map.
     *
     * @return An iterator one-past-the-end of the index map.
     **/
    inline const_iterator end() const {
      return index_map_.end();
    }
  
    /**
     * @brief Adds a new index entry to the index map.
     * @warning Must call before hasIndexWithName() and hasIndexWithDescription().
     * @note This method assumes that the caller has already acquired the
     *       necessary locks before invoking it.
     *
     * @param index_name The name of index to add (key).
     * @param index_description The index description for this index (value).
     **/
    bool addIndexMapEntry(const std::string &index_name,
                          IndexSubBlockDescription &&index_description) {  // NOLINT(whitespace/operators)
      if (index_map_.find(index_name) != index_map_.end()) {
        return false;  // index_name is already present!
      }
      // Value for this index_map key is the index description provided.
      index_map_.emplace(index_name, std::move(index_description));
      return true;
    }
  
   private:
    // A map of index names to their index description.
    std::unordered_map<std::string, IndexSubBlockDescription> index_map_;
  
    DISALLOW_COPY_AND_ASSIGN(IndexScheme);
  };
  
  /** @} */
  
  }  // namespace quickstep
  
  #endif  // QUICKSTEP_CATALOG_INDEX_SCHEME_HPP_
  