sortix-mirror/kernel/include/sortix/kernel/sortedlist.h

323 lines
6.0 KiB
C++

/*
* 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 <assert.h>
#include <stddef.h>
#include <stdint.h>
namespace Sortix {
template <class T> 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 <class Searchee>
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