Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
File renamed without changes.
143 changes: 104 additions & 39 deletions include/kf/ext/timsort.h → include/kf/algorithm/timsort.h
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
#pragma once
#include "kf/stl/new"
/*
* Taken from https://github.com/swenson/sort
* Revision: 05fd77bfec049ce8b7c408c4d3dd2d51ee061a15
Expand Down Expand Up @@ -51,14 +53,12 @@ namespace timsort
/* adapted from Hacker's Delight */
inline int clzll(uint64_t x)
{
int n;

if (x == 0)
{
return 64;
}

n = 0;
int n = 0;

if (x <= 0x00000000FFFFFFFFL)
{
Expand Down Expand Up @@ -137,14 +137,19 @@ namespace timsort
size_t r = size - 1;
size_t c = r >> 1;

if (dst == nullptr || size == 0)
{
return 0;
}

/* check for out of bounds at the beginning. */
if (cmp(x, dst[0]) < 0)
{
return 0;
}
else if (cmp(x, dst[r]) > 0)
else if (cmp(x, dst[r]) >= 0)
{
return r;
return size;
}

T* cx = &dst[c];
Expand Down Expand Up @@ -181,6 +186,11 @@ namespace timsort
template<class T>
inline void binary_insertion_sort_start(T* dst, const size_t start, const size_t size)
{
if (dst == nullptr || size == 0)
{
return;
}

for (size_t i = start; i < size; i++)
{
/* If this entry is already correct, just move along */
Expand Down Expand Up @@ -211,6 +221,11 @@ namespace timsort
template<class T>
inline void reverse_elements(T* dst, size_t start, size_t end)
{
if (dst == nullptr)
{
return;
}

while (true)
{
if (start >= end)
Expand All @@ -227,6 +242,11 @@ namespace timsort
template<class T>
inline size_t count_run(T* dst, const size_t start, const size_t size)
{
if (dst == nullptr || size == 0)
{
return 0;
}

size_t curr;

if (size - start == 1)
Expand All @@ -251,7 +271,7 @@ namespace timsort
/* increasing run */
while (true)
{
if (curr == size - 1)
if (curr == size)
{
break;
}
Expand All @@ -271,7 +291,7 @@ namespace timsort
/* decreasing run */
while (true)
{
if (curr == size - 1)
if (curr == size)
{
break;
}
Expand All @@ -292,6 +312,11 @@ namespace timsort

inline int check_invariant(TIM_SORT_RUN_T* stack, const int stack_curr)
{
if (stack == nullptr)
{
return -1;
}

if (stack_curr < 2)
{
return 1;
Expand Down Expand Up @@ -332,11 +357,24 @@ namespace timsort
template<class T>
inline void tim_sort_resize(TEMP_STORAGE_T<T>* store, const size_t new_size)
{
if (store == nullptr)
{
return;
}

if (store->alloc < new_size)
{
T* tempstore = (T*)malloc(new_size * sizeof(T));
memcpy(tempstore, store->storage, store->alloc);
detail::free(store->storage);
if (tempstore == nullptr)
{
return;
}

if (store->storage != nullptr)
{
memcpy(tempstore, store->storage, store->alloc * sizeof(T));
detail::free(store->storage);
}

store->storage = tempstore;
store->alloc = new_size;
Expand Down Expand Up @@ -418,47 +456,49 @@ namespace timsort
}

template<class T>
inline int tim_sort_collapse(T* dst, TIM_SORT_RUN_T* stack, int stack_curr, TEMP_STORAGE_T<T>* store, const size_t size)
inline int tim_sort_collapse(T* dst, TIM_SORT_RUN_T* stack, int stack_size, TEMP_STORAGE_T<T>* store, const size_t size)
{
while (true)
{
/* if the stack only has one thing on it, we are done with the collapse */
if (stack_curr <= 1)
if (stack_size <= 1)
{
break;
}

/* if this is the last merge, just do it */
if ((stack_curr == 2) && (stack[0].length + stack[1].length == size))
if ((stack_size == 2) && (stack[0].length + stack[1].length == size))
{
tim_sort_merge(dst, stack, stack_curr, store);
tim_sort_merge(dst, stack, stack_size, store);
stack[0].length += stack[1].length;
stack_curr--;
stack[1] = { 0, 0 };
stack_size--;
break;
}
/* check if the invariant is off for a stack of 2 elements */
else if ((stack_curr == 2) && (stack[0].length <= stack[1].length))
else if ((stack_size == 2) && (stack[0].length <= stack[1].length))
{
tim_sort_merge(dst, stack, stack_curr, store);
tim_sort_merge(dst, stack, stack_size, store);
stack[0].length += stack[1].length;
stack_curr--;
stack[1] = { 0, 0 };
stack_size--;
break;
}
else if (stack_curr == 2)
else if (stack_size == 2)
{
break;
}

size_t A;
size_t B = stack[stack_curr - 3].length;
size_t C = stack[stack_curr - 2].length;
size_t D = stack[stack_curr - 1].length;
size_t B = stack[stack_size - 3].length;
size_t C = stack[stack_size - 2].length;
size_t D = stack[stack_size - 1].length;

int ABC, BCD, CD;

if (stack_curr >= 4)
if (stack_size >= 4)
{
A = stack[stack_curr - 4].length;
A = stack[stack_size - 4].length;
ABC = (A <= B + C);
}
else
Expand All @@ -478,21 +518,23 @@ namespace timsort
/* left merge */
if (BCD && !CD)
{
tim_sort_merge(dst, stack, stack_curr - 1, store);
stack[stack_curr - 3].length += stack[stack_curr - 2].length;
stack[stack_curr - 2] = stack[stack_curr - 1];
stack_curr--;
tim_sort_merge(dst, stack, stack_size - 1, store);
stack[stack_size - 3].length += stack[stack_size - 2].length;
stack[stack_size - 2] = stack[stack_size - 1];
stack[stack_size - 1] = { 0, 0 };
stack_size--;
}
else
{
/* right merge */
tim_sort_merge(dst, stack, stack_curr, store);
stack[stack_curr - 2].length += stack[stack_curr - 1].length;
stack_curr--;
tim_sort_merge(dst, stack, stack_size, store);
stack[stack_size - 2].length += stack[stack_size - 1].length;
stack[stack_size - 1] = { 0, 0 };
stack_size--;
}
}

return stack_curr;
return stack_size;
}

template<class T>
Expand All @@ -501,36 +543,49 @@ namespace timsort
TEMP_STORAGE_T<T>* store,
const size_t minrun,
TIM_SORT_RUN_T* run_stack,
size_t* stack_curr,
size_t* stack_size,
size_t* curr)
{

if (dst == nullptr ||
store == nullptr ||
run_stack == nullptr ||
stack_size == nullptr ||
curr == nullptr)
{
return 0;
}

size_t len = count_run(dst, *curr, size);
size_t run = minrun;

/* If there is less than minrun left until the end of the array */
if (run > size - *curr)
{
run = size - *curr;
}

/* If the found sorted run is smaller than minrun,
* we need to extend this segment by sorting the rest of the elements to match the run size */
if (run > len)
{
binary_insertion_sort_start(&dst[*curr], len, run);
len = run;
}

run_stack[*stack_curr].start = *curr;
run_stack[*stack_curr].length = len;
(*stack_curr)++;
run_stack[*stack_size].start = *curr;
run_stack[*stack_size].length = len;
(*stack_size)++;
*curr += len;

if (*curr == size)
{
/* finish up */
while (*stack_curr > 1)
while (*stack_size > 1)
{
tim_sort_merge(dst, run_stack, static_cast<int>(*stack_curr), store);
run_stack[*stack_curr - 2].length += run_stack[*stack_curr - 1].length;
(*stack_curr)--;
tim_sort_merge(dst, run_stack, static_cast<int>(*stack_size), store);
run_stack[*stack_size - 2].length += run_stack[*stack_size - 1].length;
(*stack_size)--;
}

if (store->storage)
Expand All @@ -550,6 +605,11 @@ namespace timsort
template<class T>
inline void binary_insertion_sort(T* dst, const size_t size)
{
if (dst == nullptr)
{
return;
}

/* don't bother sorting an array of size <= 1 */
if (size <= 1)
{
Expand All @@ -562,6 +622,11 @@ namespace timsort
template<class T>
inline void tim_sort(T* dst, const size_t size)
{
if (dst == nullptr)
{
return;
}

/* don't bother sorting an array of size 1 */
if (size <= 1)
{
Expand Down
72 changes: 72 additions & 0 deletions test/Algorithm.cpp
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please rename to AlgorithmTest

Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
#include "pch.h"
#include <kf/algorithm/Algorithm.h>
#include <kf/USimpleString.h>
#include <kf/stl/vector>

SCENARIO("Algorithm binary_search_it")
{
GIVEN("A sorted vector of USimpleString values")
{
kf::USimpleString first (L"AAA");
kf::USimpleString second(L"BBB");
kf::USimpleString third (L"CCC");
kf::USimpleString last (L"DDD");

kf::USimpleString notInVec(L"FFF");

kf::vector<kf::USimpleString, NonPagedPoolNx> vec;
vec.push_back(first);
vec.push_back(second);
vec.push_back(third);
vec.push_back(last);

WHEN("Searching for existing string")
{
THEN("Returns iterator to the element")
{
auto it = kf::binary_search_it(vec.begin(), vec.end(), second);
REQUIRE(it != vec.end());
REQUIRE(it->equals(second));
}
}

WHEN("Searching for non-existing string")
{
THEN("Returns end iterator")
{
auto it = binary_search_it(vec.begin(), vec.end(), notInVec);
REQUIRE(it == vec.end());
}
}

WHEN("Searching for the first element")
{
THEN("Returns iterator to beginning")
{
auto it = binary_search_it(vec.begin(), vec.end(), first);
REQUIRE(it == vec.begin());
REQUIRE(it->equals(first));
}
}

WHEN("Searching for the last element")
{
THEN("Returns iterator to the end - 1")
{
auto it = binary_search_it(vec.begin(), vec.end(), last);
REQUIRE(it == vec.end() - 1);
REQUIRE(it->equals(last));
}
}

WHEN("Searching in empty vector")
{
THEN("Returns iterator to the end")
{
kf::vector<kf::USimpleString, NonPagedPoolNx> empty;
auto it = binary_search_it(empty.begin(), empty.end(), third);
REQUIRE(it == empty.end());
}
}
}
}
2 changes: 2 additions & 0 deletions test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ wdk_add_driver(kf-test WINVER NTDDI_WIN10 STL
HexTest.cpp
MapTest.cpp
Vector.cpp
Algorithm.cpp
timsort.cpp
)

target_link_libraries(kf-test kf::kf kmtest::kmtest)
Expand Down
Loading