String view

This commit is contained in:
2026-09-12 07:28:46 -05:00
parent 2725166ca4
commit 07bf13ec37
9 changed files with 773 additions and 68 deletions
+15
View File
@@ -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
+16 -2
View File
@@ -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 */
+6 -32
View File
@@ -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 */
+3 -17
View File
@@ -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.
+188
View File
@@ -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 */
+3 -17
View File
@@ -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.
+165
View File
@@ -0,0 +1,165 @@
#include <string.h>
#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;
}
+375
View File
@@ -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);
}
+2
View File
@@ -0,0 +1,2 @@
#include "malunal/microtest.h"
MICROTEST_MAIN()