/* This file is part of Raul. Copyright 2007-2012 David Robillard Raul is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or any later version. Raul is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Raul. If not, see . */ #ifndef RAUL_TABLE_IMPL_HPP #define RAUL_TABLE_IMPL_HPP #include #include #include #include #include #include "raul/Table.hpp" namespace Raul { /* FIXME: This could be a lot less code... */ #ifdef TABLE_SORT_DEBUG template bool Table::is_sorted() const { if (size() <= 1) return true; K prev_key = _entries[0].first; for (size_t i=1; i < size(); ++i) if (_entries[i].first < prev_key) return false; else prev_key = _entries[i].first; return true; } #endif /** Binary search (O(log(n))) */ template typename Table::const_iterator Table::find(const K& key) const { return ((Table*)this)->find(key); } /** Binary search (O(log(n))) */ template typename Table::iterator Table::find(const K& key) { return find(begin(), end(), key); } /** Binary search (O(log(end - start))) */ template typename Table::const_iterator Table::find(const_iterator start, const_iterator finish, const K& key) const { return ((Table*)this)->find(start, finish, key); } /** Binary search (O(log(end - start))) */ template typename Table::iterator Table::find(const_iterator start, const_iterator finish, const K& key) { if (size() == 0) return end(); size_t lower = start._index; size_t upper = finish._index - 1; size_t i; while (upper >= lower) { i = lower + ((upper - lower) / 2); const Entry& elem = _entries[i]; if (elem.first == key) return iterator(*this, i); else if (i < size()-1 && elem.first < key) lower = i + 1; else if (i > 0) upper = i - 1; else break; } return end(); } /** Find the end of a range using a custom comparator. * Two entries a, b are considered in the range if comp(a, b) returns true. * * Returns an iterator exactly one entry past the end of the range (possibly end()). * * WARNING: The restrictions on \a comparator are very strict: ALL items * considered equal by \a comparator must be stored in the Table consecutively * i.e. there are no 3 values a, b, c S.T. comp(a) && ! comp(b) && comp(c). * * This is useful for very quickly finding all children of a Path, which * obey the above rule with lexicographical order. */ template typename Table::const_iterator Table::find_range_end(const_iterator start, bool (*comp)(const K&,const K&)) const { return (const_cast&>(*this)).find_range_end(*((iterator*)&start), comp); } /** Find the end of a range using a custom comparator. * Two entries a, b are considered in the range if comp(a, b) returns true. * * Returns an iterator exactly one entry past the end of the range (possibly end()). * * WARNING: The restrictions on \a comparator are very strict: ALL items * considered equal by \a comparator must be stored in the Table consecutively * i.e. there are no 3 values a, b, c S.T. comp(a) && ! comp(b) && comp(c). * * This is useful for very quickly finding all children of a Path, which * obey the above rule with lexicographical order. */ template typename Table::iterator Table::find_range_end(iterator start, bool (*comp)(const K&,const K&)) { if (size() == 0 || start == end()) return this->end(); const K& key = start->first; size_t lower = start._index; size_t upper = size() - 1; if (lower == upper) { if (comp(key, _entries[lower].first)) return iterator(*this, lower+1); else return this->end(); } size_t i; while (upper > lower) { i = lower + ((upper - lower) / 2); if (upper - lower == 1) { if (comp(key, _entries[upper].first) && upper < size()) return iterator(*this, upper+1); else if (lower < size()) return iterator(*this, lower+1); } const Entry& elem = _entries[i]; // Hit if (comp(key, elem.first)) { if (i == size()-1 || !comp(key, _entries[i+1].first)) return iterator(*this, i+1); else lower = i; // Miss } else { upper = i; } } assert(comp(start->first, _entries[lower].first)); assert(lower == size()-1 || !comp(start->first, _entries[lower+1].first)); return iterator(*this, lower+1); } /** Erase and return a range of entries */ template SharedPtr< Table > Table::yank(iterator start, iterator end) { SharedPtr< Table > ret(new Table(end._index - start._index)); for (size_t i=start._index; i < end._index; ++i) ret->_entries.at(i - start._index) = _entries[i]; erase(start, end); return ret; } /** Cram a range of entries back in. * Range MUST follow the same ordering guidelines as find_range_end. * Return type is the same as insert, iterator points to first inserted entry */ template std::pair::iterator, bool> Table::cram(const Table& range) { /* FIXME: _way_ too slow */ const size_t orig_size = size(); if (range.size() == 0) return std::make_pair(end(), false); std::pair ret = insert(range._entries.front()); if (range.size() == 1) return ret; const size_t insert_index = ret.first._index; std::vector new_entries(orig_size + range.size()); for (size_t i=0; i <= insert_index; ++i) new_entries.at(i) = _entries.at(i); for (size_t i=0; i < size() - insert_index; ++i) new_entries.at(new_entries.size() - 1 - i) = _entries.at(size() - 1 - i); for (size_t i=1; i < range.size(); ++i) new_entries.at(insert_index + i) = range._entries.at(i); _entries = new_entries; assert(size() == orig_size + range.size()); #ifdef TABLE_SORT_DEBUG assert(is_sorted()); #endif return std::make_pair(iterator(*this, insert_index), true); } /** Add an item to the table, using \a entry.first as the search key. * An iterator to the element where value was set is returned, and a bool which * is true if an insertion took place, or false if an existing entry was updated. * Matches std::map::insert interface. * O(n) worst case * O(log(n)) best case (capacity is large enough) */ template std::pair::iterator, bool> Table::insert(const std::pair& entry) { const K& key = entry.first; const T& value = entry.second; if (size() == 0 || (size() == 1 && _entries[0].first < key)) { _entries.push_back(entry); return std::make_pair(iterator(*this, size()-1), true); } else if (size() == 1) { _entries.push_back(_entries[0]); _entries[0] = entry; return std::make_pair(begin(), true); } size_t lower = 0; size_t upper = size() - 1; size_t i; // Find the earliest element > key while (upper >= lower) { i = lower + ((upper - lower) / 2); assert(i >= lower); assert(i <= upper); assert(_entries[lower].first <= _entries[i].first); assert(_entries[i].first <= _entries[upper].first); assert(i < size()); Entry& elem = _entries[i]; if (elem.first == key) { elem.second = value; return std::make_pair(iterator(*this, i), false); } else if (key < elem.first) { if (i == 0 || _entries[i-1].first < key) break; upper = i - 1; } else { lower = i + 1; } } // Lil' off by one touchup :) if (i < size() && _entries[i].first <= key) ++i; _entries.push_back(_entries.back()); // Shift everything beyond i right for (size_t j = size()-2; j > i; --j) _entries[j] = _entries[j-1]; _entries[i] = entry; #ifdef TABLE_SORT_DEBUG assert(is_sorted()); #endif return std::make_pair(iterator(*this, i), true); } /** Insert an item, and return a reference to it's value. * * This may be used to insert values with pretty syntax: * * table["gorilla"] = "killa"; * * T must have a default constructor for this to be possible. */ template T& Table::operator[](const K& key) { iterator i = find(key); if (i != end()) { return i->second; } else { std::pair ret = insert(std::make_pair(key, T())); return ret.first->second; } } template void Table::erase(const K& key) { erase(find(key)); } template void Table::erase(iterator i) { if (i == end()) return; const size_t index = i._index; // Shift left for (size_t j=index; j < size()-1; ++j) _entries[j] = _entries[j+1]; _entries.pop_back(); #ifdef TABLE_SORT_DEBUG assert(is_sorted()); #endif } /** Erase a range of elements from \a first to \a last, including first but * not including last. */ template void Table::erase(iterator first, iterator last) { const size_t first_index = first._index; const size_t last_index = last._index; Table::erase_by_index(first_index, last_index); } /** Erase a range of elements from \a first_index to \a last_index, including * first_index but not including last_index. */ template void Table::erase_by_index(size_t first_index, size_t last_index) { const size_t num_removed = last_index - first_index; // Shift left for (size_t j=first_index; j < size() - num_removed; ++j) _entries[j] = _entries[j + num_removed]; _entries.resize(size() - num_removed); #ifdef TABLE_SORT_DEBUG assert(is_sorted()); #endif } } // namespace Raul #endif // RAUL_TABLE_IMLP_HPP