/****************************************************************************** * Copyright (c) 2000-2021 Ericsson Telecom AB * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v2.0 * which accompanies this distribution, and is available at * https://www.eclipse.org/org/documents/epl-2.0/EPL-2.0.html * * Contributors: * Balasko, Jeno * Delic, Adam * Forstner, Matyas * Gecse, Roland * Koppany, Csaba * Raduly, Csaba * Szabados, Kristof * Szabo, Janos Zoltan – initial implementation * ******************************************************************************/ #ifndef _Common_map_HH #define _Common_map_HH #include "error.h" #include "../common/dbgnew.hh" /** * This container associates a \e key with its elements, and is * optimized to access the elements referenced by their keys. * The keys -- in contrast with the elements -- are owned by * the container. The keys in this container are unique. * * Accessing an element by its key has logarithmic cost. * Adding or removing an element has linear cost (proportional to the number * of elements in the map). * \pre * \arg The type of the key (<em>T_key</em>) must be a type which has * equality operator (==) and less-than operator (<). Other * comparison operators are not assumed. * \arg \a T_key must have copy constructor and destructor. * \arg See also * \ref container_concepts "General rules and concepts about containers". * * There is a possibility to iterate through all * elements using integral indices. This index of an element can * change when inserting/deleting other elements. Accessing elements * by this integral index is not cost-optimal. * * \ingroup containers */ template<class T_key, class T> class map { private: struct map_struct { T_key key; T *dat; map_struct(const T_key& p_key, T *p_dat) : key(p_key), dat(p_dat) { } map_struct(const map_struct& other) : key(other.key), dat(other.dat) {} private: map_struct& operator=(const map_struct& other); }; size_t num_m; ///< Number of elements (size) size_t max_m; ///< Available storage (capacity) /// Cache for the last search result. /// This will remember the index of the element last searched for; /// thus after checking the existence of the item, reaching it /// won't be logarithmical but constant mutable size_t last_searched_key; /// Array of pointers to map data. It is kept sorted (ascending) by keys. map_struct **m_ptr; static const size_t initial_size = 1, increment_factor = 2; /** Copy constructor: DO NOT IMPLEMENT! */ map(const map&); /** Copy assignment: DO NOT IMPLEMENT! */ map& operator=(const map&); public: static const size_t max_map_length = static_cast<size_t>( -1 ); /** Creates an empty map. */ map() : num_m(0), max_m(0), last_searched_key(0), m_ptr(NULL) { } /** Deallocates its memory, including the keys. * If the container is not empty, FATAL_ERROR occurs, * so before destructing, clear() must be invoked explicitly. */ ~map() { if (num_m > 0) FATAL_ERROR("map:~map(): map is not empty"); Free(m_ptr); } /** Returns the number of elements in the container. */ size_t size() const { return num_m; } /** Returns true if the container has no elements. */ bool empty() const { return num_m == 0; } /** Erases the entire container. */ void clear() { for (size_t r = 0; r < num_m; r++) delete m_ptr[r]; num_m = 0; last_searched_key = 0; } /** Adds the elem \a T identified by \a key to the container. * If an element with the given key already exists, FATAL_ERROR occurs. */ void add(const T_key& key, T *elem) { size_t l = 0; if (num_m > 0) { size_t r = num_m - 1; while (l < r) { // binary search size_t m = l + (r - l) / 2; if (m_ptr[m]->key < key) l = m + 1; else r = m; } if (m_ptr[l]->key < key) l++; else if (m_ptr[l]->key == key) FATAL_ERROR("map::add(): key already exists"); if (num_m >= max_m) { // Array is full if (num_m > max_m) FATAL_ERROR("map::add(): num_m > max_m"); if (max_m <= max_map_length / increment_factor) max_m *= increment_factor; // room for doubling else if (max_m < max_map_length) max_m = max_map_length; else FATAL_ERROR("map::add(): cannot enlarge map"); m_ptr = static_cast<map_struct**> (Realloc(m_ptr, max_m * sizeof(*m_ptr))); } memmove(m_ptr + l + 1, m_ptr + l, (num_m - l) * sizeof(*m_ptr)); } else if (m_ptr == NULL) { max_m = initial_size; m_ptr = static_cast<map_struct**>(Malloc(initial_size * sizeof(*m_ptr))); } m_ptr[l] = new map_struct(key, elem); num_m++; last_searched_key = num_m; } /** Returns the index of k within *m_ptr[] or num_m if key was not found */ size_t find_key(const T_key& k) const { if (last_searched_key < num_m && m_ptr[last_searched_key]->key == k) return last_searched_key; else if (num_m == 0) return 0; size_t l = 0, r = num_m - 1; while (l < r) { size_t m = l + (r - l) / 2; if (m_ptr[m]->key < k) l = m + 1; else r = m; } if (m_ptr[l]->key == k) last_searched_key = l; else last_searched_key = num_m; return last_searched_key; } /** Erases the element identified by \a key. If no such element, * then silently ignores the request. */ void erase(const T_key& key) { size_t n = find_key(key); if (n < num_m) { delete m_ptr[n]; num_m--; memmove(m_ptr + n, m_ptr + n + 1, (num_m - n) * sizeof(*m_ptr)); last_searched_key = num_m; } } /** Returns true if an element with the given \a key * already exists. */ bool has_key(const T_key& key) const { return find_key(key) < num_m; } /** Returns the copy of the key contained in the map. * * The copy of the key may be longer-lived than the argument. * They are otherwise identical (operator== would return true). * * @param \a key * @return copy of \a key contained in the map * @pre key must exist in the map */ const T_key& get_key(const T_key& key) const { size_t n = find_key(key); if (n >= num_m) FATAL_ERROR("map::get_key() const: key not found"); return m_ptr[n]->key; } /** Returns the element identified by \a key. * If no such element, then a FATAL_ERROR occurs. */ T *operator[](const T_key& key) const { size_t n = find_key(key); if (n >= num_m) FATAL_ERROR("map::operator[]() const: key not found"); return m_ptr[n]->dat; } /** Returns the element identified by \a key. * If no such element, then a FATAL_ERROR occurs. * \note This member can not be used to add new elements to * the collection. */ T*& operator[](const T_key& key) { size_t n = find_key(key); if (n >= num_m) FATAL_ERROR("map::operator[]() const: key not found"); return m_ptr[n]->dat; } /** Returns the <em>n</em>th element. This is used to iterate through * the ordered list of elements. Elements are ordered by their keys. * If \a n >= size(), then a FATAL_ERROR occurs. * The key of the element is accessible via get_nth_key(). */ T *get_nth_elem(size_t n) const { if (n >= num_m) FATAL_ERROR("map::get_nth_elem() const: index overflow"); /* do not break the previous line, suncc doesn't like it... */ return m_ptr[n]->dat; } /** Returns the <em>n</em>th element. This is used to iterate through * the elements. If \a n >= size(), then a FATAL_ERROR occurs. * The key of the element is accessible via get_nth_key(). */ T*& get_nth_elem(size_t n) { if (n >= num_m) FATAL_ERROR("map::get_nth_elem(): index overflow"); return m_ptr[n]->dat; } /** Returns the key of the <em>n</em>th element. * This is used to iterate through the keys of elements. * If \a n >= size(), then a FATAL_ERROR occurs. * \note There is only \c const version of this member. If you * want to modify the key of an element, you have to erase it from * the container, then add with the new key. */ const T_key& get_nth_key(size_t n) const { if (n >= num_m) FATAL_ERROR("map::get_nth_key() const: index overflow"); return m_ptr[n]->key; } }; #endif // _Common_map_HH