diff options
-rw-r--r-- | include/strlist.h | 3 | ||||
-rw-r--r-- | lib/strlist.c | 113 | ||||
-rw-r--r-- | tests/test_strlist.c | 178 |
3 files changed, 235 insertions, 59 deletions
diff --git a/include/strlist.h b/include/strlist.h index 4cf76c6..919bdc1 100644 --- a/include/strlist.h +++ b/include/strlist.h @@ -29,6 +29,7 @@ unsigned short strlist_item_as_ushort(StrList *pStrList, size_t index); short strlist_item_as_short(StrList *pStrList, size_t index); unsigned char strlist_item_as_uchar(StrList *pStrList, size_t index); char strlist_item_as_char(StrList *pStrList, size_t index); +unsigned char strlist_item_as_uchar(StrList *pStrList, size_t index); char *strlist_item_as_str(StrList *pStrList, size_t index); char *strlist_item(StrList *pStrList, size_t index); void strlist_set(StrList *pStrList, size_t index, char *value); @@ -37,6 +38,8 @@ void strlist_reverse(StrList *pStrList); void strlist_sort(StrList *pStrList, unsigned int mode); void strlist_append_strlist(StrList *pStrList1, StrList *pStrList2); void strlist_append(StrList *pStrList, char *str); +StrList *strlist_copy(StrList *pStrList); +int strlist_cmp(StrList *a, StrList *b); void strlist_free(StrList *pStrList); #endif //SPM_STRLIST_H diff --git a/lib/strlist.c b/lib/strlist.c index 5c1f0e2..b8d394d 100644 --- a/lib/strlist.c +++ b/lib/strlist.c @@ -47,6 +47,23 @@ void strlist_append(StrList *pStrList, char *str) { } /** + * Produce a new copy of a `StrList` + * @param pStrList `StrList` + * @return `StrList` copy + */ +StrList *strlist_copy(StrList *pStrList) { + StrList *result = strlist_init(); + if (pStrList == NULL || result == NULL) { + return NULL; + } + + for (size_t i = 0; i < strlist_count(pStrList); i++) { + strlist_append(result, strlist_item(pStrList, i)); + } + return result; +} + +/** * Append the contents of a `StrList` to another `StrList` * @param pStrList1 `StrList` * @param pStrList2 `StrList` @@ -66,44 +83,35 @@ void strlist_append_strlist(StrList *pStrList1, StrList *pStrList2) { } /** - * - * @param a - * @param b - * @return + * Compare two `StrList`s + * @param a `StrList` structure + * @param b `StrList` structure + * @return same=0, different=1, error=-1 (a is NULL), -2 (b is NULL) */ -static int _strlist_cmpfn(const void *a, const void *b) { - const char *aa = *(const char**)a; - const char *bb = *(const char**)b; - int result = strcmp(aa, bb); - return result; -} +int strlist_cmp(StrList *a, StrList *b) { + if (a == NULL) { + return -1; + } -/** - * - * @param a - * @param b - * @return - */ -static int _strlist_asc_cmpfn(const void *a, const void *b) { - const char *aa = *(const char**)a; - const char *bb = *(const char**)b; - size_t len_a = strlen(aa); - size_t len_b = strlen(bb); - return len_a > len_b - strcmp(aa, bb); -} + if (b == NULL) { + return -2; + } -/** - * - * @param a - * @param b - * @return - */ -static int _strlist_dsc_cmpfn(const void *a, const void *b) { - const char *aa = *(const char**)a; - const char *bb = *(const char**)b; - size_t len_a = strlen(aa); - size_t len_b = strlen(bb); - return len_a < len_b - strcmp(aa, bb); + if (a->num_alloc != b->num_alloc) { + return 1; + } + + if (a->num_inuse != b->num_inuse) { + return 1; + } + + for (size_t i = 0; i < strlist_count(a); i++) { + if (strcmp(strlist_item(a, i), strlist_item(b, i)) != 0) { + return 1; + } + } + + return 0; } /** @@ -119,20 +127,7 @@ void strlist_sort(StrList *pStrList, unsigned int mode) { return; } - switch (mode) { - case STRLIST_ASC: - fn = _strlist_asc_cmpfn; - break; - case STRLIST_DSC: - fn = _strlist_dsc_cmpfn; - break; - case STRLIST_DEFAULT: - default: - fn = _strlist_cmpfn; - break; - } - - qsort(pStrList->data, pStrList->num_inuse, sizeof(char *), fn); + strsort(pStrList->data, mode); } /** @@ -225,7 +220,7 @@ char *strlist_item_as_str(StrList *pStrList, size_t index) { * @return `char` */ char strlist_item_as_char(StrList *pStrList, size_t index) { - return (char) *(strlist_item(pStrList, index)); + return (char) strtol(strlist_item(pStrList, index), NULL, 10); } /** @@ -235,7 +230,7 @@ char strlist_item_as_char(StrList *pStrList, size_t index) { * @return `unsigned char` */ unsigned char strlist_item_as_uchar(StrList *pStrList, size_t index) { - return (unsigned char) *(strlist_item(pStrList, index)); + return (unsigned char) strtol(strlist_item(pStrList, index), NULL, 10); } /** @@ -245,7 +240,7 @@ unsigned char strlist_item_as_uchar(StrList *pStrList, size_t index) { * @return `short` */ short strlist_item_as_short(StrList *pStrList, size_t index) { - return (short)atoi(strlist_item(pStrList, index)); + return (short)strtol(strlist_item(pStrList, index), NULL, 10); } /** @@ -255,7 +250,7 @@ short strlist_item_as_short(StrList *pStrList, size_t index) { * @return `unsigned short` */ unsigned short strlist_item_as_ushort(StrList *pStrList, size_t index) { - return (unsigned short)atoi(strlist_item(pStrList, index)); + return (unsigned short)strtoul(strlist_item(pStrList, index), NULL, 10); } /** @@ -265,7 +260,7 @@ unsigned short strlist_item_as_ushort(StrList *pStrList, size_t index) { * @return `int` */ int strlist_item_as_int(StrList *pStrList, size_t index) { - return atoi(strlist_item(pStrList, index)); + return (int)strtol(strlist_item(pStrList, index), NULL, 10); } /** @@ -275,7 +270,7 @@ int strlist_item_as_int(StrList *pStrList, size_t index) { * @return `unsigned int` */ unsigned int strlist_item_as_uint(StrList *pStrList, size_t index) { - return (unsigned int)atoi(strlist_item(pStrList, index)); + return (unsigned int)strtoul(strlist_item(pStrList, index), NULL, 10); } /** @@ -285,7 +280,7 @@ unsigned int strlist_item_as_uint(StrList *pStrList, size_t index) { * @return `long` */ long strlist_item_as_long(StrList *pStrList, size_t index) { - return atol(strlist_item(pStrList, index)); + return strtol(strlist_item(pStrList, index), NULL, 10); } /** @@ -295,7 +290,7 @@ long strlist_item_as_long(StrList *pStrList, size_t index) { * @return `unsigned long` */ unsigned long strlist_item_as_ulong(StrList *pStrList, size_t index) { - return (unsigned long)atol(strlist_item(pStrList, index)); + return strtoul(strlist_item(pStrList, index), NULL, 10); } /** @@ -305,7 +300,7 @@ unsigned long strlist_item_as_ulong(StrList *pStrList, size_t index) { * @return `long long` */ long long strlist_item_as_long_long(StrList *pStrList, size_t index) { - return (long long)atoll(strlist_item(pStrList, index)); + return strtoll(strlist_item(pStrList, index), NULL, 10); } /** @@ -315,7 +310,7 @@ long long strlist_item_as_long_long(StrList *pStrList, size_t index) { * @return `unsigned long long` */ unsigned long long strlist_item_as_ulong_long(StrList *pStrList, size_t index) { - return (unsigned long long)atoll(strlist_item(pStrList, index)); + return strtoull(strlist_item(pStrList, index), NULL, 10); } /** diff --git a/tests/test_strlist.c b/tests/test_strlist.c new file mode 100644 index 0000000..a4ea647 --- /dev/null +++ b/tests/test_strlist.c @@ -0,0 +1,178 @@ +#include "spm.h" +#include "framework.h" + +static const char *story_truth = "The quick brown fox jumps over the lazy dog."; +static const char *story_truth_rev = "dog. jumps over the lazy fox The quick brown"; +static const char *story_truth_sort_asc = "fox dog. The quick brown jumps over the lazy"; +static const char *story_truth_sort_dsc = "jumps over the lazy The quick brown dog. fox"; + +static char *DATA[] = { + "The quick brown", + "fox", + "jumps over the lazy", + "dog.", + NULL, +}; + +long int MAX_INTS[] = { + INT8_MAX, + UINT8_MAX, + INT16_MAX, + UINT16_MAX, + INT32_MAX, + UINT32_MAX, + INT64_MAX, + UINT64_MAX, + 0, +}; + +enum truthValue { + int8_max = 0, + uint8_max, + int16_max, + uint16_max, + int32_max, + uint32_max, + int64_max, + uint64_max, +}; + + +int main(int argc, char *argv[]) { + const char *storyFmt = "expected story: '%s', but got '%s'\n"; + char *story = NULL; + union TestValue testValue = {0,}; + StrList *strList = NULL; + StrList *strListCopy = NULL; + StrList *strListNumbers = NULL; + StrList truthInit = {1, 0, NULL}; + size_t used = 0; + size_t allocated = 0; + char intStr[255]; + const int DATA_SIZE = (sizeof(DATA) / sizeof(char *)) - 1; + + + // Initialize string list and check initial state + strList = strlist_init(); + myassert(strList->num_inuse == 0, "strList has wrong number of records in use: %zu (expected %zu)\n", strList->num_inuse, truthInit.num_inuse); + myassert(strList->num_alloc == 1, "strList has wrong number of records allocated: %zu (expected %zu)\n", strList->num_alloc, truthInit.num_alloc); + myassert(strList->data != NULL, "strList was NULL"); + + ssize_t count = strlist_count(strList); + myassert(count == 0, "strlist_count returned wrong number of records in use: %zu (expected %zu)\n", count, strList->num_inuse); + + // Populate list with strings + for (size_t i = 0; DATA[i] != NULL; i++) { + used = i + 1; + allocated = used + 1; + + strlist_append(strList, DATA[i]); + myassert(strList->num_inuse == used, "incorrect used record count post-append\n"); + myassert(strList->num_alloc == allocated, "incorrect allocated record count post-append\n"); + } + + // Is the data represented in the array as we expect it to be? + story = join(strList->data, " "); + myassert(strcmp(story, story_truth) == 0, storyFmt, story_truth, story); + free(story); + story = NULL; + + // Copy the array (because we're about to modify it) + strListCopy = strlist_copy(strList); + myassert(strListCopy != NULL, "strlist_copy failed\n"); + + // Does reversing the array work correctly? + strlist_reverse(strListCopy); + + story = join(strListCopy->data, " "); + // Copy the array (because we're about to modify it) + myassert(strcmp(story, story_truth_rev) == 0, storyFmt, story_truth, story); + free(story); + story = NULL; + strlist_free(strListCopy); + strListCopy = NULL; + + // Copy the array (because we're about to modify it) + strListCopy = strlist_copy(strList); + myassert(strListCopy != NULL, "strlist_copy failed\n"); + + // Now compare the arrays to make sure they're identical + myassert(strlist_cmp(strList, strListCopy) == 0, "strlist_copy result does not match original StrList contents"); + + // Sort the array to see if it works. + strlist_sort(strListCopy, SPM_SORT_LEN_ASCENDING); + + // The array just got modified, so check to make sure they are NOT identical + myassert(strlist_cmp(strList, strListCopy) == 1, "StrList data matches original StrList contents (after modification)"); + + story = join(strListCopy->data, " "); + myassert(strcmp(story, story_truth_sort_asc) == 0, storyFmt, story_truth_sort_asc, story); + free(story); + story = NULL; + strlist_free(strListCopy); + strListCopy = NULL; + + // Copy the array (because we're about to modify it) + strListCopy = strlist_copy(strList); + myassert(strListCopy != NULL, "strlist_copy failed\n"); + + // Sort the array once more using a different comparator + strlist_sort(strListCopy, SPM_SORT_LEN_DESCENDING); + + story = join(strListCopy->data, " "); + myassert(strcmp(story, story_truth_sort_dsc) == 0, storyFmt, story_truth_sort_dsc, story); + free(story); + story = NULL; + strlist_free(strListCopy); + strListCopy = NULL; + + // Now append numerical values (as string) so we can start reading them back + for (size_t i = 0; MAX_INTS[i] != 0; i++) { + memset(intStr, '\0', sizeof(intStr)); + sprintf(intStr, "%zu", MAX_INTS[i]); + strlist_append(strList, intStr); + } + + memset(intStr, '\0', sizeof(intStr)); + sprintf(intStr, "%lf", MAXFLOAT); + strlist_append(strList, intStr); + + // Now make sure values derived from strlist_item_as_*() functions work properly + // NOTE: My focus is on 64-bit, so if you're compiling this on a 32-bit computer + // and these tests fail for you... That's a shame. + testValue.signed_char = strlist_item_as_char(strList, DATA_SIZE + int8_max); + myassert(testValue.signed_char == INT8_MAX, "int8_max incorrect: %d", testValue.signed_char); + + testValue.unsigned_char = strlist_item_as_uchar(strList, DATA_SIZE + uint8_max); + myassert(testValue.unsigned_char == UINT8_MAX, "uint8_max incorrect: %d", testValue.unsigned_char); + + testValue.signed_short = strlist_item_as_short(strList, DATA_SIZE + int16_max); + myassert(testValue.signed_short == INT16_MAX, "int16_max incorrect: %d", testValue.signed_short); + + testValue.unsigned_short = strlist_item_as_ushort(strList, DATA_SIZE + uint16_max); + myassert(testValue.unsigned_short == UINT16_MAX, "uint16_max incorrect: %d", testValue.unsigned_short); + + testValue.signed_int = strlist_item_as_int(strList, DATA_SIZE + int32_max); + myassert(testValue.signed_int == INT32_MAX, "int32_max incorrect: %d", testValue.signed_int); + + testValue.unsigned_int = strlist_item_as_uint(strList, DATA_SIZE + uint32_max); + myassert(testValue.unsigned_int == UINT32_MAX, "uint32_max incorrect: %d", testValue.unsigned_int); + + testValue.signed_long = strlist_item_as_long(strList, DATA_SIZE + int64_max); + myassert(testValue.signed_long == INT64_MAX, "int64_max (long) incorrect: %ld", testValue.signed_long); + + testValue.unsigned_long = strlist_item_as_ulong(strList, DATA_SIZE + uint64_max); + myassert(testValue.unsigned_long == UINT64_MAX, "uint64_max (long) incorrect: %lu", testValue.unsigned_long); + + testValue.signed_long_long = strlist_item_as_long_long(strList, DATA_SIZE + int64_max); + myassert(testValue.signed_long_long == INT64_MAX, "int64_max (long long) incorrect: %lld", testValue.signed_long_long); + + testValue.unsigned_long_long = strlist_item_as_ulong_long(strList, DATA_SIZE + uint64_max); + myassert(testValue.unsigned_long_long == UINT64_MAX, "uint64_max (long long) incorrect: %llu", testValue.unsigned_long_long); + + testValue.floating = strlist_item_as_float(strList, DATA_SIZE + uint64_max + 1); + myassert(testValue.floating == MAXFLOAT, "floating point maximum incorrect: %f", testValue.floating); + + strlist_free(strList); + return 0; +}
\ No newline at end of file |