/* * Copyright (c) 2011, 2012, 2014 Jonas 'Sortie' Termansen. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * sortix/kernel/sortedlist.h * A container that ensures its elements are always sorted when they are * accessed. It also provides binary search. */ #ifndef _INCLUDE_SORTIX_KERNEL_SORTEDLIST_H #define _INCLUDE_SORTIX_KERNEL_SORTEDLIST_H #include #include #include namespace Sortix { template class SortedList { public: // GCC appears to be broken as the const in the function pointer typedef // below is ignored for some reason. Is it a compiler bug? typedef int (*compare_t)(const T, const T); private: static const int FLAG_SORTED = 1 << 0; private: T* list; size_t listused; size_t listlength; compare_t comparator; unsigned flags; public: SortedList(compare_t thecomparator = NULL) : list(NULL), listused(0), listlength(0), comparator(thecomparator), flags(0) { } ~SortedList() { Clear(); } public: void SetComparator(compare_t thecomparator) { comparator = thecomparator; flags &= ~FLAG_SORTED; } void Clear() { for ( size_t i = 0; i < listused; i++ ) list[i].~T(); listused = 0; flags |= FLAG_SORTED; delete[] list; list = NULL; } size_t Length() { return listused; } bool Empty() { return !listused; } bool IsSorted() { return flags & FLAG_SORTED; } void Sort(compare_t thecomparator) { comparator = thecomparator; Sort(); } void Sort() { if ( !listused || (flags & FLAG_SORTED) ) return; MergeSort(0, listused-1); flags |= FLAG_SORTED; } void Invalidate() { flags &= ~FLAG_SORTED; } bool Add(T t) { if ( listused == listlength && !Expand() ) return false; list[listused++] = t; flags &= ~FLAG_SORTED; return true; } T Get(size_t index) { if ( !(flags & FLAG_SORTED) ) Sort(); return list[index]; } // Accesses the elements, but the order might not be sorted. T GetUnsorted(size_t index) { return list[index]; } T Remove(size_t index) { if ( !(flags & FLAG_SORTED) ) Sort(); assert(index < listused); // TODO: It may be possible to further speed up removal by delaying // the expensive memory copy operation. T result = list[index]; list[index].~T(); for ( size_t i = index+1; i < listused; i++ ) { list[i-1] = list[i]; list[i].~T(); } listused--; return result; } size_t Search(T searchee) { return Search(comparator, searchee); } // Returns the index of the element being searched for using the given // comparator, or returns SIZE_MAX if not found. template size_t Search(int (*searcher)(const T t, const Searchee searchee), const Searchee searchee) { if ( !listused ) return SIZE_MAX; if ( !(flags & FLAG_SORTED) ) Sort(); size_t minindex = 0; size_t maxindex = listused-1; do { size_t tryindex = (minindex + maxindex) / 2; const T& t = list[tryindex]; int relation = searcher(t, searchee); if ( relation == 0 ) return tryindex; if ( relation < 0 ) minindex = tryindex+1; if ( relation > 0 ) maxindex = tryindex-1; } while ( maxindex + 1 != minindex ); return SIZE_MAX; } private: bool Expand() { size_t newsize = (listused == 0 ) ? 4 : listlength*2; T* newlist = new T[newsize*2]; if ( !newlist ) return false; for ( size_t i = 0; i < listused; i++ ) { newlist[i] = list[i]; list[i].~T(); } delete[] list; list = newlist; listlength = newsize; return true; } T* GetDestList() { return list + listlength; } void MergeSort(size_t minindex, size_t maxindex) { FixupMergeSort(minindex, maxindex); } void FixupMergeSort(size_t minindex, size_t maxindex) { MergeSortInternal(minindex, maxindex); T* destlist = GetDestList(); do list[minindex] = destlist[minindex]; while ( minindex++ != maxindex ); } void MergeSortInternal(size_t minindex, size_t maxindex) { T* destlist = GetDestList(); if ( maxindex - minindex == 1 ) { if ( 0 < comparator(list[minindex], list[maxindex]) ) { destlist[minindex] = list[maxindex]; destlist[maxindex] = list[minindex]; list[minindex].~T(); list[maxindex].~T(); } else { destlist[minindex] = list[minindex]; destlist[maxindex] = list[maxindex]; list[minindex].~T(); list[maxindex].~T(); } return; } if ( minindex == maxindex ) { destlist[minindex] = list[minindex]; list[minindex].~T(); return; } const size_t asize = (maxindex - minindex + 1) / 2 + 1; const size_t bsize = (maxindex - minindex) / 2; const size_t astart = minindex; const size_t bstart = minindex + asize; const size_t aend = minindex + asize - 1; const size_t bend = minindex + asize + bsize - 1; FixupMergeSort(astart, aend); FixupMergeSort(bstart, bend); destlist += minindex; size_t aindex = astart; size_t bindex = bstart; while ( aindex <= aend && bindex <= bend ) { int relation = comparator(list[aindex], list[bindex]); if ( relation <= 0 ) { *destlist++ = list[aindex]; list[aindex++].~T(); } else { *destlist++ = list[bindex]; list[bindex++].~T(); } } while ( aindex <= aend ) { *destlist++ = list[aindex]; list[aindex++].~T(); } while ( bindex <= bend ) { *destlist++ = list[bindex]; list[bindex++].~T(); } } }; } // namespace Sortix #endif