#ifndef VARLEN_SORT_INCLUDED #define VARLEN_SORT_INCLUDED /* Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, as published by the Free Software Foundation. This program is also distributed with certain software (including but not limited to OpenSSL) that is licensed under separate terms, as designated in a particular file or component or in included license documentation. The authors of MySQL hereby grant you an additional permission to link the program and your derivative works with the separately licensed software that they have included with MySQL. This program 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, version 2.0, for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ /** A RandomAccessIterator that splits up a char/uchar array into fixed-length elements, so that they can be sorted using std::sort. There is also a helper function varlen_sort() that is an adapter around std::sort for this purpose. */ #include "my_dbug.h" #include "template_utils.h" #include #include #include /* Conceptually similar to a struct { uchar[N] }, except that most of the time, it just holds a reference to an underlying array, instead of keeping the memory itself. */ struct varlen_element { varlen_element(unsigned char *ptr_arg, size_t elem_size_arg) : ptr(ptr_arg), elem_size(elem_size_arg) {} varlen_element(varlen_element &other) = delete; /* In this case, we need to own the memory ourselves. It is really only used when std::sort wants to do an insertion sort and needs a temporary element. */ varlen_element(varlen_element &&other) : elem_size(other.elem_size) { if (other.mem != nullptr) { mem = std::move(other.mem); } else { mem.reset(new unsigned char[other.elem_size]); memcpy(mem.get(), other.ptr, elem_size); } ptr = mem.get(); } varlen_element &operator=(const varlen_element &other) = delete; varlen_element &operator=(varlen_element &&other) { DBUG_ASSERT(elem_size == other.elem_size); memcpy(ptr, other.ptr, elem_size); return *this; } std::unique_ptr mem; unsigned char *ptr = nullptr; size_t elem_size = 0; }; // ValueSwappable. static inline void swap(const varlen_element &a, const varlen_element &b) { DBUG_ASSERT(a.elem_size == b.elem_size); std::swap_ranges(a.ptr, a.ptr + a.elem_size, b.ptr); } // Conceptually similar to a _pointer_ to an uchar[N]. class varlen_iterator { public: varlen_iterator(unsigned char *ptr_arg, size_t elem_size_arg) : ptr(ptr_arg), elem_size(elem_size_arg) {} // Iterator (required for InputIterator). varlen_element operator*() const { return varlen_element{ptr, elem_size}; } varlen_iterator &operator++() { ptr += elem_size; return *this; } // EqualityComparable (required for InputIterator). bool operator==(const varlen_iterator &other) const { return ptr == other.ptr && elem_size == other.elem_size; } // InputIterator (required for ForwardIterator). bool operator!=(const varlen_iterator &other) const { return !(*this == other); } varlen_element operator->() const { return varlen_element{ptr, elem_size}; } // DefaultConstructible (required for ForwardIterator). varlen_iterator() {} // ForwardIterator (required for RandomAccessIterator). varlen_iterator operator++(int) { varlen_iterator copy = *this; ptr += elem_size; return copy; } // BidirectionalIterator (required for RandomAccessIterator). varlen_iterator &operator--() { ptr -= elem_size; return *this; } varlen_iterator operator--(int) { varlen_iterator copy = *this; ptr -= elem_size; return copy; } // RandomAccessIterator. varlen_iterator &operator+=(size_t n) { ptr += elem_size * n; return *this; } varlen_iterator &operator-=(size_t n) { ptr -= elem_size * n; return *this; } varlen_iterator operator+(size_t n) const { return varlen_iterator{ptr + elem_size * n, elem_size}; } varlen_iterator operator-(size_t n) const { return varlen_iterator{ptr - elem_size * n, elem_size}; } ptrdiff_t operator-(const varlen_iterator &other) const { DBUG_ASSERT(elem_size == other.elem_size); DBUG_ASSERT((ptr - other.ptr) % elem_size == 0); return (ptr - other.ptr) / elem_size; } varlen_element operator[](size_t i) const { return varlen_element{ptr + i * elem_size, elem_size}; } bool operator<(varlen_iterator &other) const { DBUG_ASSERT(elem_size == other.elem_size); return ptr < other.ptr; } bool operator>(varlen_iterator &other) const { DBUG_ASSERT(elem_size == other.elem_size); return ptr > other.ptr; } bool operator>=(varlen_iterator &other) const { DBUG_ASSERT(elem_size == other.elem_size); return ptr >= other.ptr; } bool operator<=(varlen_iterator &other) const { DBUG_ASSERT(elem_size == other.elem_size); return ptr <= other.ptr; } private: unsigned char *ptr = nullptr; size_t elem_size = 0; }; namespace std { // Required for Iterator. template <> struct iterator_traits : iterator_traits {}; } // namespace std /* Compare should be a functor that takes in two T*. T does not need to be char or uchar. */ template inline void varlen_sort(T *first, T *last, size_t elem_size, Compare comp) { std::sort(varlen_iterator(pointer_cast(first), elem_size), varlen_iterator(pointer_cast(last), elem_size), [comp](const varlen_element &a, const varlen_element &b) { return comp(pointer_cast(a.ptr), pointer_cast(b.ptr)); }); } #endif // !defined(VARLEN_SORT_INCLUDED)