From c8b4c7b5fd31a2d9554975c5b862da696e44bca9 Mon Sep 17 00:00:00 2001 From: Ben Pfaff Date: Wed, 1 Dec 2004 01:30:54 +0000 Subject: [PATCH] Add qsort(), bsearch(). --- TODO | 2 - src/lib/stdlib.c | 175 ++++++++++++++++++++++++++++++++++++- src/lib/stdlib.h | 14 +++ src/tests/threads/stdlib.c | 113 ++++++++++++++++++++++++ 4 files changed, 301 insertions(+), 3 deletions(-) create mode 100644 src/tests/threads/stdlib.c diff --git a/TODO b/TODO index b90b2d8..27c6254 100644 --- a/TODO +++ b/TODO @@ -12,8 +12,6 @@ * Some confusion--do we really get overlapping ro/rw segment in normal link? Student example seemed to show that we don't. -* Add qsort(), bsearch() implementations to kernel. - * Finish writing the tour. * Come up with a way for us to release some of the tests. diff --git a/src/lib/stdlib.c b/src/lib/stdlib.c index 56a94c6..0056fc1 100644 --- a/src/lib/stdlib.c +++ b/src/lib/stdlib.c @@ -1,4 +1,6 @@ #include +#include +#include #include #include @@ -10,8 +12,10 @@ atoi (const char *s) bool negative; int value; + ASSERT (s != NULL); + /* Skip white space. */ - while (isspace (*s)) + while (isspace ((unsigned char) *s)) s++; /* Parse sign. */ @@ -35,3 +39,172 @@ atoi (const char *s) return value; } + +/* Compares A and B by calling the AUX function. */ +static int +compare_thunk (const void *a, const void *b, void *aux) +{ + int (**compare) (const void *, const void *) = aux; + return (*compare) (a, b); +} + +/* Sorts ARRAY, which contains CNT elements of SIZE bytes each, + using COMPARE. When COMPARE is passed a pair of elements A + and B, respectively, it must return a strcmp()-type result, + i.e. less than zero if A < B, zero if A == B, greater than + zero if A > B. */ +void +qsort (void *array, size_t cnt, size_t size, + int (*compare) (const void *, const void *)) +{ + quick_sort (array, cnt, size, compare_thunk, &compare); +} + +/* Swaps the SIZE bytes at A and B. */ +static void +swap (unsigned char *a, unsigned char *b, size_t size) +{ + size_t i; + + for (i = 0; i < size; i++) + { + unsigned char t = a[i]; + a[i] = b[i]; + b[i] = t; + } +} + +/* Selects a random "pivot" element in ARRAY, which contains CNT + elements of SIZE bytes each, and partitions ARRAY into two + contiguous subranges around the pivot, so that the first N + elements are less than or equal to the pivot and the remaining + elements are greater than the pivot. Returns N. + + Uses COMPARE to compare elements, passing AUX as auxiliary + data. When COMPARE is passed a pair of elements A and B, + respectively, it must return a strcmp()-type result, i.e. less + than zero if A < B, zero if A == B, greater than zero if A > + B. */ +static size_t +randomized_partition (void *array, size_t cnt, size_t size, + int (*compare) (const void *, const void *, void *aux), + void *aux) +{ + unsigned char *first = array; /* Beginning of array. */ + unsigned char *last = first + size * cnt; /* End of array. */ + unsigned char *pivot = last - size; /* Element used as pivot. */ + unsigned char *middle; /* Invariant: elements on left are <= pivot. */ + unsigned char *current; + + ASSERT (array != NULL); + ASSERT (cnt > 1); + ASSERT (size > 0); + ASSERT (compare != NULL); + + /* Choose a random pivot. */ + swap (pivot, first + (random_ulong () % cnt) * size, size); + + /* Iterate through the array moving elements less than or equal + to the pivot to the middle. */ + middle = array; + for (current = first; current < last; current += size) + if (compare (current, pivot, aux) <= 0) + { + swap (middle, current, size); + middle += size; + } + + return (middle - first) / size; +} + +/* Sorts ARRAY, which contains CNT elements of SIZE bytes each, + using COMPARE to compare elements, passing AUX as auxiliary + data. When COMPARE is passed a pair of elements A and B, + respectively, it must return a strcmp()-type result, i.e. less + than zero if A < B, zero if A == B, greater than zero if A > + B. */ +void +quick_sort (void *array_, size_t cnt, size_t size, + int (*compare) (const void *, const void *, void *aux), + void *aux) +{ + unsigned char *array = array_; + + ASSERT (array != NULL || cnt == 0); + ASSERT (compare != NULL); + ASSERT (size > 0); + + if (cnt > 1) + { + size_t q = randomized_partition (array, cnt, size, compare, aux); + + /* Sort smaller partition first to guarantee O(lg n) stack + depth limit. */ + if (q < cnt - q) + { + quick_sort (array, q, size, compare, aux); + quick_sort (array + q * size, cnt - q, size, compare, aux); + } + else + { + quick_sort (array + q * size, cnt - q, size, compare, aux); + quick_sort (array, q, size, compare, aux); + } + } +} + +/* Searches ARRAY, which contains CNT elements of SIZE bytes + each, for the given KEY. Returns a match is found, otherwise + a null pointer. If there are multiple matches, returns an + arbitrary one of them. + + ARRAY must be sorted in order according to COMPARE. + + Uses COMPARE to compare elements. When COMPARE is passed a + pair of elements A and B, respectively, it must return a + strcmp()-type result, i.e. less than zero if A < B, zero if A + == B, greater than zero if A > B. */ +void * +bsearch (const void *key, const void *array, size_t cnt, + size_t size, int (*compare) (const void *, const void *)) +{ + return binary_search (key, array, cnt, size, compare_thunk, &compare); +} + +/* Searches ARRAY, which contains CNT elements of SIZE bytes + each, for the given KEY. Returns a match is found, otherwise + a null pointer. If there are multiple matches, returns an + arbitrary one of them. + + ARRAY must be sorted in order according to COMPARE. + + Uses COMPARE to compare elements, passing AUX as auxiliary + data. When COMPARE is passed a pair of elements A and B, + respectively, it must return a strcmp()-type result, i.e. less + than zero if A < B, zero if A == B, greater than zero if A > + B. */ +void * +binary_search (const void *key, const void *array, size_t cnt, size_t size, + int (*compare) (const void *, const void *, void *aux), + void *aux) +{ + const unsigned char *first = array; + const unsigned char *last = array + size * cnt; + + while (first < last) + { + size_t range = (last - first) / size; + const unsigned char *middle = first + (range / 2) * size; + int cmp = compare (key, middle, aux); + + if (cmp < 0) + last = middle; + else if (cmp > 0) + first = middle + size; + else + return (void *) middle; + } + + return NULL; +} + diff --git a/src/lib/stdlib.h b/src/lib/stdlib.h index 9455c6a..cfc12ed 100644 --- a/src/lib/stdlib.h +++ b/src/lib/stdlib.h @@ -3,6 +3,20 @@ #include +/* Standard functions. */ int atoi (const char *); +void qsort (void *array, size_t cnt, size_t size, + int (*compare) (const void *, const void *)); +void *bsearch (const void *key, const void *array, size_t cnt, + size_t size, int (*compare) (const void *, const void *)); + +/* Nonstandard functions. */ +void quick_sort (void *array, size_t cnt, size_t size, + int (*compare) (const void *, const void *, void *aux), + void *aux); +void *binary_search (const void *key, const void *array, size_t cnt, + size_t size, + int (*compare) (const void *, const void *, void *aux), + void *aux); #endif /* lib/stdlib.h */ diff --git a/src/tests/threads/stdlib.c b/src/tests/threads/stdlib.c new file mode 100644 index 0000000..fc15481 --- /dev/null +++ b/src/tests/threads/stdlib.c @@ -0,0 +1,113 @@ +/* Test program for sorting and searching in lib/kernel/stdlib.c. + + Attempts to test the sorting and searching functionality that + is not sufficiently tested elsewhere in Pintos. + + This is not a test we will run on your submitted projects. + It is here for completeness. +*/ + +#undef NDEBUG +#include +#include +#include +#include +#include +#include "threads/test.h" + +/* Maximum number of elements in an array that we will test. */ +#define MAX_CNT 4096 + +static void shuffle (int[], size_t); +static int compare_ints (const void *, const void *); +static void verify_order (const int[], size_t); +static void verify_bsearch (const int[], size_t); + +/* Test sorting and searching implementations. */ +void +test (void) +{ + int cnt; + + printf ("testing various size arrays:"); + for (cnt = 0; cnt < MAX_CNT; cnt = cnt * 4 / 3 + 1) + { + int repeat; + + printf (" %zu", cnt); + for (repeat = 0; repeat < 10; repeat++) + { + static int values[MAX_CNT]; + int i; + + /* Put values 0...CNT in random order in VALUES. */ + for (i = 0; i < cnt; i++) + values[i] = i; + shuffle (values, cnt); + + /* Sort VALUES, then verify ordering. */ + qsort (values, cnt, sizeof *values, compare_ints); + verify_order (values, cnt); + verify_bsearch (values, cnt); + } + } + + printf (" done\n"); +} + +/* Shuffles the CNT elements in ARRAY into random order. */ +static void +shuffle (int *array, size_t cnt) +{ + size_t i; + + for (i = 0; i < cnt; i++) + { + size_t j = i + random_ulong () % (cnt - i); + int t = array[j]; + array[j] = array[i]; + array[i] = t; + } +} + +/* Returns 1 if *A is greater than *B, + 0 if *A equals *B, + -1 if *A is less than *B. */ +static int +compare_ints (const void *a_, const void *b_) +{ + const int *a = a_; + const int *b = b_; + + return *a < *b ? -1 : *a > *b; +} + +/* Verifies that ARRAY contains the CNT ints 0...CNT-1. */ +static void +verify_order (const int *array, size_t cnt) +{ + int i; + + for (i = 0; (size_t) i < cnt; i++) + ASSERT (array[i] == i); +} + +/* Checks that bsearch() works properly in ARRAY. ARRAY must + contain the values 0...CNT-1. */ +static void +verify_bsearch (const int *array, size_t cnt) +{ + int not_in_array[] = {0, -1, INT_MAX, MAX_CNT, MAX_CNT + 1, MAX_CNT * 2}; + int i; + + /* Check that all the values in the array are found properly. */ + for (i = 0; (size_t) i < cnt; i++) + ASSERT (bsearch (&i, array, cnt, sizeof *array, compare_ints) + == array + i); + + /* Check that some values not in the array are not found. */ + not_in_array[0] = cnt; + for (i = 0; (size_t) i < sizeof not_in_array / sizeof *not_in_array; i++) + ASSERT (bsearch (¬_in_array[i], array, cnt, sizeof *array, compare_ints) + == NULL); +} -- 2.30.2