diff --git a/chinookfile b/chinookfile index 84128e6..f0c5be0 100644 --- a/chinookfile +++ b/chinookfile @@ -2,12 +2,27 @@ name: types gpid: malunal semv: 1.0.0 +requires: +- remote: git@git.erasit.com:malunal/microtest + branch: v1.0.0 + targets: - name: malunal.types type: archive srcs: + - ./source/strview.c - ./source/object.c - ./source/uuid.c +tests: +- name: malunal.types.allinone + type: program + deps: + - malunal.microtest + - malunal.types + srcs: + - ./tests/strview.c + - ./tests/types.c + exports: - malunal.types diff --git a/include/malunal/type.h b/include/malunal/type.h index 734ee70..f2ac30c 100644 --- a/include/malunal/type.h +++ b/include/malunal/type.h @@ -16,9 +16,9 @@ # define MALUNAL_POINTER_SIZE __SIZEOF_POINTER__ #elif defined(_MSC_VER) # if defined(_M_X64) || defined(_M_ARM64) -# define MY_PTR_SIZE 8 +# define MALUNAL_POINTER_SIZE 8 # elif defined(_M_IX86) || defined(_M_ARM) -# define MY_PTR_SIZE 4 +# define MALUNAL_POINTER_SIZE 4 # else /* Unrecognized MSVC target */ # error "Unrecognized MSVC target architecture" # endif /* MSVC compiler check */ @@ -288,4 +288,18 @@ _Static_assert( typedef const name##_t* name##_iptr_t; \ struct name +/** + * @def define_union + * @brief Defines a union and its pointer types. + * @details This simplifies the way we typically define our unions, in which we + * define the union itself and type definitions for the mutable and + * immutable pointers to that union. + * @param name The name of the union. + */ +#define define_union(name) \ + typedef union name name##_t; \ + typedef name##_t* name##_mptr_t; \ + typedef const name##_t* name##_iptr_t; \ + union name + #endif /* MALUNAL_TYPES_HEADER */ diff --git a/include/malunal/types/error.h b/include/malunal/types/error.h index 01c94d7..ebbf388 100644 --- a/include/malunal/types/error.h +++ b/include/malunal/types/error.h @@ -21,13 +21,14 @@ /** + * @struct error_domain * @brief Defines an error domain. * @details An error domain is treated like a namespace for error codes. * It provides the name of the domain and a function to describe the * error code such that the receiver of the error knows where the error * came from and what it means. */ -typedef struct { +define_struct(error_domain) { /** * @brief A pointer to a function that is capable of describing an error code * that belongs to this domain, by providing a string message of the @@ -44,31 +45,18 @@ typedef struct { * or logging purposes, or doubly verifying the pointer source. */ malunal_cstr_t name; -} error_domain_t; - -/** - * @brief A pointer to a mutable error domain. - * @details This is provided to simplify type declarations for functions - * requiring error domains that are meant to be mutable. - */ -typedef error_domain_t* error_domain_mptr_t; - -/** - * @brief A pointer to an immutable error domain. - * @details This is provided to simplify type declarations for functions - * requiring error domains that are meant to be immutable. - */ -typedef const error_domain_t* error_domain_iptr_t; +}; /** + * @struct error * @brief Defines an error. * @details An error is the combination of a domain, describing where it came * from or where it belongs to, and the actual error code. This makes * it so error codes are distinguishable from each other, at the cost * of a minimal amount of memory overhead. */ -typedef struct { +define_struct(error) { /** * @brief The domain of the error. * @details This is where the error belong to, kind of like its namespace. It @@ -86,20 +74,6 @@ typedef struct { * is null to represent no error. */ malunal_int32_t code; -} error_t; - -/** - * @brief A pointer to a mutable error. - * @details This is provided to simplify type declarations for functions - * requiring error that are meant to be mutable. - */ -typedef error_t* error_mptr_t; - -/** - * @brief A pointer to an immutable error. - * @details This is provided to simplify type declarations for functions - * requiring error that are meant to be immutable. - */ -typedef const error_t* error_iptr_t; +}; #endif /* MALUNAL_TYPES_ERROR_HEADER */ diff --git a/include/malunal/types/object.h b/include/malunal/types/object.h index 839b868..12744a8 100644 --- a/include/malunal/types/object.h +++ b/include/malunal/types/object.h @@ -31,13 +31,14 @@ ERROR_DOMAIN_OBJECT_T; /** + * @struct object * @brief Defines the object interface object. * @details Since this is only an interface, it only contains a pointer to the * runtime type information for itself. Other objects which implement * this interface, should be able to provide its runtime type * information through this pointer. */ -typedef struct { +define_struct(object) { /** * @brief The runtime type information for this object. * @details Designed to provide information about the functional capabilities @@ -48,22 +49,7 @@ typedef struct { * function is an @c object_t pointer. */ rtti_iptr_t rtti; -} object_t; - -/** - * @brief A pointer to a mutable object. - * @details This is provided to simplify type declarations for functions - * requiring objects that are meant to be mutable. - */ -typedef object_t* object_mptr_t; - -/** - * @brief A pointer to an immutable object. - * @details This is provided to simplify type declarations for functions - * requiring objects that are meant to be immutable. - */ -typedef const object_t* object_iptr_t; - +}; /** * @brief A set of errors which an object itself may produce. diff --git a/include/malunal/types/strview.h b/include/malunal/types/strview.h new file mode 100644 index 0000000..3cb9282 --- /dev/null +++ b/include/malunal/types/strview.h @@ -0,0 +1,188 @@ +/** + * @file strview.h + * @brief Contains the definition of the string view structure and functions. + * @author John Christman (sorakatadzuma@gmail.com) + * @copyright Malunal Studios, LLC. + */ +#include "../type.h" + +#ifndef MALUNAL_TYPES_STRVIEW_HEADER +#define MALUNAL_TYPES_STRVIEW_HEADER + +/** + * @def STRVIEW_NPOS + * @brief Defines the null position of a string view. + * @details When received as a return result: indicates that the function could + * not operate on the strview provided, or indicates that the function + * would or did iterate beyond the bounds of the string view. + */ +#define STRVIEW_NPOS ((malunal_size_t)-1) + + +/** + * @struct strview + * @brief Defines a string view. + * @details A string view is a length based addressable pointer to an array of + * characters. Often, it is used in place of regular strings to remove + * the reliance on null-terminated strings and needing to store a copy + * of the null-terminated string, or its substring. + */ +define_struct(strview) { + /** + * @brief A pointer to the beginning of the string view. + * @details This points to the beginning of the string view, treating it like + * an array of characters that can be indexed. + */ + malunal_cstr_t beg; + + /** + * @brief A pointer to the ending of the string view. + * @details This points to the ending of the string view, treating it like an + * array of characters that can be indexed. + */ + malunal_cstr_t end; +}; + +/** + * @brief Creates a string view from the provided @c data and @c length. + * @param data A pointer to the data of the string view. + * @param length The length of the string view in characters. + * @returns The constructed string view. + */ +strview_t +strview( + malunal_cstr_t data, + malunal_size_t length +); + +/** + * @brief Creates a string view from the provided null-terminated @c data. + * @param data A pointer to the data of the string view. + * @returns The constructed string view. + */ +strview_t +strview_from_cstr( + malunal_cstr_t data +); + +/** + * @brief Provides the length of the string view. + * @param self A pointer to the string view to get the length of. + * @returns The length of the string view, zero if the pointer is null. + */ +malunal_size_t +strview_length( + strview_iptr_t self +); + +/** + * @brief Moves the start of the view forward by @c count. + * @param self The strview to trim by @c count. + * @param count The number of characters to trim by. + * @remarks This will mutate the provided string view directly. Provide a copy + * of the string view you wish to trim, if you want to preserve the + * original. + */ +malunal_void_t +strview_ltrim( + strview_mptr_t self, + malunal_size_t count +); + +/** + * @brief Moves the end of the view backward by @c count. + * @param self The strview to trim by @c count. + * @param count The number of characters to trim by. + * @remarks This will mutate the provided string view directly. Provide a copy + * of the string view you wish to trim, if you want to preserve the + * original. + */ +malunal_void_t +strview_rtrim( + strview_mptr_t self, + malunal_size_t count +); + +/** + * @brief Creates a subview of the current string view. + * @param self The string view to subview. + * @param start Where to subview from. + * @param count How many characters to subview. + * @returns The constructed subview. + * @remarks The result of this function can be a string view with null data and + * zero length if the input string view could not be subviewed. + */ +strview_t +strview_subview( + strview_iptr_t self, + malunal_size_t start, + malunal_size_t count +); + +/** + * @brief Provides the equality result of two string views. + * @param self The string view to compare against. + * @param other The string view to compare with. + * @retval true If the comparison of the two string views is zero. + * @retval false If the comparision of the two string views is non-zero. + */ +malunal_bool_t +strview_equals( + strview_iptr_t self, + strview_iptr_t other +); + +/** + * @brief Provides the comparison result of two string views. + * @param self The string view to compare against. + * @param other The string view to compare with. + * @retval -1 If @c self is comparatively less than @c other. + * @retval 0 If @c self is comparatively equal to @c other. + * @retval 1 if @c self is comparatively more than @c other. + */ +malunal_int32_t +strview_compare( + strview_iptr_t self, + strview_iptr_t other +); + +/** + * @brief Indicates if @c self starts with @c other. + * @param self The string view to compare against. + * @param other The string view to compare with. + * @retval true If @c self does start with @c other. + * @retval false If @c self does not start with @c other. + */ +malunal_bool_t +strview_starts_with( + strview_iptr_t self, + strview_iptr_t other +); + +/** + * @brief Indicates if @c self ends with @c other. + * @param self The string view to compare against. + * @param other The string view to compare with. + * @retval true If @c self does end with @c other. + * @retval false If @c self does not end with @c other. + */ +malunal_bool_t +strview_ends_with( + strview_iptr_t self, + strview_iptr_t other +); + +/** + * @brief Indicates if @c self contains @c other. + * @param self The string view to compare against. + * @param other The string view to compare with. + * @retval true If @c self does contain @c other. + * @retval false If @c self does not contain @c other. + */ +malunal_bool_t +strview_contains( + strview_iptr_t self, + strview_iptr_t other +); + +#endif /* MALUNAL_TYPES_STRVIEW_HEADER */ diff --git a/include/malunal/types/uuid.h b/include/malunal/types/uuid.h index 3abd9b6..95b84c3 100644 --- a/include/malunal/types/uuid.h +++ b/include/malunal/types/uuid.h @@ -11,12 +11,13 @@ /** + * @union uuid * @brief Defines a universally unique identifier structure. * @details This may be used for many things. However, for the purposes of this * library specifically it is used to help identify different types and * enable a simplistic type identification system. */ -typedef union { +define_union(uuid) { struct { malunal_uint32_t tl; malunal_uint16_t tm; @@ -27,22 +28,7 @@ typedef union { }; malunal_uint8_t bytes[16]; -} uuid_t; - -/** - * @brief A pointer to a mutable UUID. - * @details This is provided to simplify type declarations for functions - * requiring UUIDs that are meant to be mutable. - */ -typedef uuid_t* uuid_mptr_t; - -/** - * @brief A pointer to an immutable UUID. - * @details This is provided to simplify type declarations for functions - * requiring UUIDs that are meant to be immutable. - */ -typedef const uuid_t* uuid_iptr_t; - +}; /** * @brief Compares two UUIDs, checking if they are equal. diff --git a/source/strview.c b/source/strview.c new file mode 100644 index 0000000..2c10817 --- /dev/null +++ b/source/strview.c @@ -0,0 +1,165 @@ +#include +#include "malunal/types/strview.h" + + +strview_t +strview( + malunal_cstr_t data, + malunal_size_t length +) { + return (strview_t) { + .beg = data, + .end = data + length + }; +} + +strview_t +strview_from_cstr( + malunal_cstr_t data +) { + return data != null + ? strview(data, strlen(data)) + : strview(null, 0); +} + +malunal_size_t +strview_length( + strview_iptr_t self +) { + return self != null + ? self->end - self->beg + : 0; +} + +malunal_void_t +strview_ltrim( + strview_mptr_t self, + malunal_size_t count +) { + malunal_size_t length = + strview_length(self); + if (length == 0) + return; + + if (length < count) + count = length; + self->beg += count; +} + +malunal_void_t +strview_rtrim( + strview_mptr_t self, + malunal_size_t count +) { + malunal_size_t length = + strview_length(self); + if (length == 0) + return; + + if (length < count) + count = length; + self->end -= count; +} + +strview_t +strview_subview( + strview_iptr_t self, + malunal_size_t start, + malunal_size_t count +) { + if (self == null) + return strview(null, 0); + + strview_t result = { self->beg, self->end }; + strview_ltrim(&result, start); + + malunal_size_t length = strview_length(&result); + strview_rtrim(&result, length - count); + return result; +} + +malunal_bool_t +strview_equals( + strview_iptr_t self, + strview_iptr_t other +) { + return strview_compare(self, other) == 0; +} + +malunal_int32_t +strview_compare( + strview_iptr_t self, + strview_iptr_t other +) { + // Check pointers. + if (self < other) return -1; + if (self > other) return 1; + if (self == other) return 0; + + // Check internals. + if (self->beg < other->beg) return -1; + if (self->beg > other->beg) return 1; + if (self->end < other->end) return -1; + if (self->end > other->end) return 1; + + // They are equal by internals. + return 0; +} + +malunal_bool_t +strview_starts_with( + strview_iptr_t self, + strview_iptr_t other +) { + if (self == null) return false; + if (other == null) return true; + + malunal_size_t hlen = strview_length(self); + malunal_size_t nlen = strview_length(other); + return hlen >= nlen + ? strncmp(self->beg, other->beg, nlen) == 0 + : false; +} + +malunal_bool_t +strview_ends_with( + strview_iptr_t self, + strview_iptr_t other +) { + if (self == null) return false; + if (other == null) return true; + + malunal_size_t hlen = strview_length(self); + malunal_size_t nlen = strview_length(other); + if (hlen < nlen) + return false; + + malunal_size_t diff = hlen - nlen; + malunal_cstr_t beg = self->beg + diff; + return strncmp(beg, other->beg, nlen) == 0; +} + +malunal_bool_t +strview_contains( + strview_iptr_t self, + strview_iptr_t other +) { + if (self == null) return false; + if (other == null) return true; + + malunal_size_t hlen = strview_length(self); + malunal_size_t nlen = strview_length(other); + if (hlen < nlen) + return false; + + // Poor man's sliding window + malunal_cstr_t beg = self->beg; + while (hlen >= nlen) { + if (strncmp(beg, other->beg, nlen)) + return true; + beg++; + hlen--; + } + + return false; +} diff --git a/tests/strview.c b/tests/strview.c new file mode 100644 index 0000000..7f69116 --- /dev/null +++ b/tests/strview.c @@ -0,0 +1,375 @@ +#include "malunal/microtest.h" +#include "malunal/types/strview.h" + +/* ------------------------------------------------------------------------- + * Construction: strview() / strview_from_cstr() + * ---------------------------------------------------------------------- */ + +MICROTEST(strview_construct, from_data_and_length) { + malunal_cstr_t data = "hello world"; + strview_t view = strview(data, 5); + + MICROTEST_EXPECT_EQ(view.beg, data); + MICROTEST_EXPECT_EQ(view.end, data + 5); + MICROTEST_EXPECT_EQ(strview_length(&view), (malunal_size_t)5); +} + +MICROTEST(strview_construct, zero_length) { + malunal_cstr_t data = "hello"; + strview_t view = strview(data, 0); + + MICROTEST_EXPECT_EQ(strview_length(&view), (malunal_size_t)0); + MICROTEST_EXPECT_EQ(view.beg, view.end); +} + +MICROTEST(strview_construct, from_cstr_matches_strlen) { + malunal_cstr_t data = "hello world"; + strview_t view = strview_from_cstr(data); + + MICROTEST_EXPECT_EQ(strview_length(&view), (malunal_size_t)strlen(data)); + MICROTEST_EXPECT_TRUE(memcmp(view.beg, data, strview_length(&view)) == 0); +} + +MICROTEST(strview_construct, from_cstr_empty) { + strview_t view = strview_from_cstr(""); + MICROTEST_EXPECT_EQ(strview_length(&view), (malunal_size_t)0); +} + +MICROTEST(strview_construct, from_cstr_matches_manual_construction) { + malunal_cstr_t data = "matching"; + strview_t fromcstr = strview_from_cstr(data); + strview_t manual = strview(data, (malunal_size_t)strlen(data)); + + MICROTEST_EXPECT_TRUE(strview_equals(&fromcstr, &manual)); +} + +/* ------------------------------------------------------------------------- + * strview_length() + * ---------------------------------------------------------------------- */ + +MICROTEST(strview_length, reports_correct_length) { + strview_t view = strview_from_cstr("0123456789"); + MICROTEST_EXPECT_EQ(strview_length(&view), (malunal_size_t)10); +} + +MICROTEST(strview_length, null_self_returns_zero) { + MICROTEST_EXPECT_EQ(strview_length(null), (malunal_size_t)0); +} + +/* ------------------------------------------------------------------------- + * strview_ltrim() + * ---------------------------------------------------------------------- */ + +MICROTEST(strview_ltrim, moves_start_forward) { + strview_t view = strview_from_cstr("hello world"); + strview_ltrim(&view, 6); + + MICROTEST_EXPECT_EQ(strview_length(&view), (malunal_size_t)5); + MICROTEST_EXPECT_TRUE(memcmp(view.beg, "world", 5) == 0); +} + +MICROTEST(strview_ltrim, zero_count_is_noop) { + strview_t view = strview_from_cstr("unchanged"); + strview_t original = view; + + strview_ltrim(&view, 0); + + MICROTEST_EXPECT_TRUE(strview_equals(&view, &original)); +} + +MICROTEST(strview_ltrim, full_length_yields_empty_view) { + strview_t view = strview_from_cstr("hello"); + strview_ltrim(&view, strview_length(&view)); + + MICROTEST_EXPECT_EQ(strview_length(&view), (malunal_size_t)0); +} + +/* ------------------------------------------------------------------------- + * strview_rtrim() + * ---------------------------------------------------------------------- */ + +MICROTEST(strview_rtrim, moves_end_backward) { + strview_t view = strview_from_cstr("hello world"); + strview_rtrim(&view, 6); + + MICROTEST_EXPECT_EQ(strview_length(&view), (malunal_size_t)5); + MICROTEST_EXPECT_TRUE(memcmp(view.beg, "hello", 5) == 0); +} + +MICROTEST(strview_rtrim, zero_count_is_noop) { + strview_t view = strview_from_cstr("unchanged"); + strview_t original = view; + + strview_rtrim(&view, 0); + + MICROTEST_EXPECT_TRUE(strview_equals(&view, &original)); +} + +MICROTEST(strview_rtrim, full_length_yields_empty_view) { + strview_t view = strview_from_cstr("hello"); + strview_rtrim(&view, strview_length(&view)); + + MICROTEST_EXPECT_EQ(strview_length(&view), (malunal_size_t)0); +} + +MICROTEST(strview_rtrim, combined_with_ltrim_isolates_middle) { + strview_t view = strview_from_cstr(" trimmed "); + strview_ltrim(&view, 2); + strview_rtrim(&view, 2); + + MICROTEST_EXPECT_EQ(strview_length(&view), (malunal_size_t)7); + MICROTEST_EXPECT_TRUE(memcmp(view.beg, "trimmed", 7) == 0); +} + +/* ------------------------------------------------------------------------- + * strview_subview() + * ---------------------------------------------------------------------- */ + +MICROTEST(strview_subview, middle_slice) { + strview_t view = strview_from_cstr("hello world"); + strview_t sub = strview_subview(&view, 6, 5); + + MICROTEST_EXPECT_EQ(strview_length(&sub), (malunal_size_t)5); + MICROTEST_EXPECT_TRUE(memcmp(sub.beg, "world", 5) == 0); +} + +MICROTEST(strview_subview, from_start) { + strview_t view = strview_from_cstr("hello world"); + strview_t sub = strview_subview(&view, 0, 5); + + MICROTEST_EXPECT_TRUE(memcmp(sub.beg, "hello", 5) == 0); +} + +MICROTEST(strview_subview, zero_count_is_empty) { + strview_t view = strview_from_cstr("hello world"); + strview_t sub = strview_subview(&view, 3, 0); + + MICROTEST_EXPECT_EQ(strview_length(&sub), (malunal_size_t)0); +} + +MICROTEST(strview_subview, out_of_bounds_start_fails) { + /* Per the header remarks, an invalid subview yields null data and zero + * length rather than reading past the end of the source view. */ + strview_t view = strview_from_cstr("hello"); + strview_t sub = strview_subview(&view, 100, 1); + + MICROTEST_EXPECT_EQ(strview_length(&sub), (malunal_size_t)0); + MICROTEST_EXPECT_NULL(sub.beg); +} + +MICROTEST(strview_subview, count_exceeding_bounds_fails) { + strview_t view = strview_from_cstr("hello"); + strview_t sub = strview_subview(&view, 3, 10); + + MICROTEST_EXPECT_EQ(strview_length(&sub), (malunal_size_t)0); + MICROTEST_EXPECT_NULL(sub.beg); +} + +MICROTEST(strview_subview, null_self_fails) { + strview_t sub = strview_subview(null, 0, 1); + + MICROTEST_EXPECT_EQ(strview_length(&sub), (malunal_size_t)0); + MICROTEST_EXPECT_NULL(sub.beg); +} + +/* ------------------------------------------------------------------------- + * strview_equals() + * ---------------------------------------------------------------------- */ + +MICROTEST(strview_equals, identical_content_is_equal) { + strview_t a = strview_from_cstr("identical"); + strview_t b = strview_from_cstr("identical"); + + MICROTEST_EXPECT_TRUE(strview_equals(&a, &b)); +} + +MICROTEST(strview_equals, different_content_is_not_equal) { + strview_t a = strview_from_cstr("hello"); + strview_t b = strview_from_cstr("world"); + + MICROTEST_EXPECT_FALSE(strview_equals(&a, &b)); +} + +MICROTEST(strview_equals, different_lengths_are_not_equal) { + strview_t a = strview_from_cstr("hello"); + strview_t b = strview_from_cstr("hello!"); + + MICROTEST_EXPECT_FALSE(strview_equals(&a, &b)); +} + +MICROTEST(strview_equals, empty_views_are_equal) { + strview_t a = strview_from_cstr(""); + strview_t b = strview_from_cstr(""); + + MICROTEST_EXPECT_TRUE(strview_equals(&a, &b)); +} + +MICROTEST(strview_equals, is_reflexive) { + strview_t a = strview_from_cstr("self"); + + MICROTEST_EXPECT_TRUE(strview_equals(&a, &a)); +} + +/* ------------------------------------------------------------------------- + * strview_compare() + * ---------------------------------------------------------------------- */ + +MICROTEST(strview_compare, equal_views_compare_zero) { + strview_t a = strview_from_cstr("apple"); + strview_t b = strview_from_cstr("apple"); + + MICROTEST_EXPECT_EQ(strview_compare(&a, &b), 0); +} + +MICROTEST(strview_compare, lesser_view_compares_negative) { + strview_t a = strview_from_cstr("apple"); + strview_t b = strview_from_cstr("banana"); + + MICROTEST_EXPECT_LT(strview_compare(&a, &b), 0); +} + +MICROTEST(strview_compare, greater_view_compares_positive) { + strview_t a = strview_from_cstr("banana"); + strview_t b = strview_from_cstr("apple"); + + MICROTEST_EXPECT_GT(strview_compare(&a, &b), 0); +} + +MICROTEST(strview_compare, shorter_prefix_compares_negative) { + strview_t a = strview_from_cstr("app"); + strview_t b = strview_from_cstr("apple"); + + MICROTEST_EXPECT_LT(strview_compare(&a, &b), 0); +} + +/* ------------------------------------------------------------------------- + * strview_starts_with() + * ---------------------------------------------------------------------- */ + +MICROTEST(strview_starts_with, true_when_prefix_matches) { + strview_t self = strview_from_cstr("hello world"); + strview_t other = strview_from_cstr("hello"); + + MICROTEST_EXPECT_TRUE(strview_starts_with(&self, &other)); +} + +MICROTEST(strview_starts_with, false_when_prefix_differs) { + strview_t self = strview_from_cstr("hello world"); + strview_t other = strview_from_cstr("world"); + + MICROTEST_EXPECT_FALSE(strview_starts_with(&self, &other)); +} + +MICROTEST(strview_starts_with, false_when_other_longer_than_self) { + strview_t self = strview_from_cstr("hi"); + strview_t other = strview_from_cstr("hello"); + + MICROTEST_EXPECT_FALSE(strview_starts_with(&self, &other)); +} + +MICROTEST(strview_starts_with, true_for_empty_other) { + strview_t self = strview_from_cstr("hello"); + strview_t other = strview_from_cstr(""); + + MICROTEST_EXPECT_TRUE(strview_starts_with(&self, &other)); +} + +MICROTEST(strview_starts_with, true_when_equal) { + strview_t self = strview_from_cstr("hello"); + strview_t other = strview_from_cstr("hello"); + + MICROTEST_EXPECT_TRUE(strview_starts_with(&self, &other)); +} + +/* ------------------------------------------------------------------------- + * strview_ends_with() + * ---------------------------------------------------------------------- */ + +MICROTEST(strview_ends_with, true_when_suffix_matches) { + strview_t self = strview_from_cstr("hello world"); + strview_t other = strview_from_cstr("world"); + + MICROTEST_EXPECT_TRUE(strview_ends_with(&self, &other)); +} + +MICROTEST(strview_ends_with, false_when_suffix_differs) { + strview_t self = strview_from_cstr("hello world"); + strview_t other = strview_from_cstr("hello"); + + MICROTEST_EXPECT_FALSE(strview_ends_with(&self, &other)); +} + +MICROTEST(strview_ends_with, false_when_other_longer_than_self) { + strview_t self = strview_from_cstr("hi"); + strview_t other = strview_from_cstr("hello"); + + MICROTEST_EXPECT_FALSE(strview_ends_with(&self, &other)); +} + +MICROTEST(strview_ends_with, true_for_empty_other) { + strview_t self = strview_from_cstr("hello"); + strview_t other = strview_from_cstr(""); + + MICROTEST_EXPECT_TRUE(strview_ends_with(&self, &other)); +} + +MICROTEST(strview_ends_with, true_when_equal) { + strview_t self = strview_from_cstr("hello"); + strview_t other = strview_from_cstr("hello"); + + MICROTEST_EXPECT_TRUE(strview_ends_with(&self, &other)); +} + +/* ------------------------------------------------------------------------- + * strview_contains() + * ---------------------------------------------------------------------- */ + +MICROTEST(strview_contains, true_when_substring_present) { + strview_t self = strview_from_cstr("hello world"); + strview_t other = strview_from_cstr("lo wo"); + + MICROTEST_EXPECT_TRUE(strview_contains(&self, &other)); +} + +MICROTEST(strview_contains, false_when_substring_absent) { + strview_t self = strview_from_cstr("hello world"); + strview_t other = strview_from_cstr("xyz"); + + MICROTEST_EXPECT_FALSE(strview_contains(&self, &other)); +} + +MICROTEST(strview_contains, true_for_prefix) { + strview_t self = strview_from_cstr("hello world"); + strview_t other = strview_from_cstr("hello"); + + MICROTEST_EXPECT_TRUE(strview_contains(&self, &other)); +} + +MICROTEST(strview_contains, true_for_suffix) { + strview_t self = strview_from_cstr("hello world"); + strview_t other = strview_from_cstr("world"); + + MICROTEST_EXPECT_TRUE(strview_contains(&self, &other)); +} + +MICROTEST(strview_contains, true_for_empty_other) { + strview_t self = strview_from_cstr("hello"); + strview_t other = strview_from_cstr(""); + + MICROTEST_EXPECT_TRUE(strview_contains(&self, &other)); +} + +MICROTEST(strview_contains, false_when_other_longer_than_self) { + strview_t self = strview_from_cstr("hi"); + strview_t other = strview_from_cstr("hello"); + + MICROTEST_EXPECT_FALSE(strview_contains(&self, &other)); +} + +/* ------------------------------------------------------------------------- + * STRVIEW_NPOS + * ---------------------------------------------------------------------- */ + +MICROTEST(strview_npos, is_max_size_value) { + MICROTEST_EXPECT_EQ(STRVIEW_NPOS, (malunal_size_t)-1); +} diff --git a/tests/types.c b/tests/types.c new file mode 100644 index 0000000..134b53d --- /dev/null +++ b/tests/types.c @@ -0,0 +1,2 @@ +#include "malunal/microtest.h" +MICROTEST_MAIN()