#include #include #include #include "malunal/strview.h" #define STRVIEW_NUMBUF_SIZE 64 static malunal_cstr_t describe(malunal_int32_t code) { switch (code) { case STRVIEW_ERROR_NULL_INPUT: return "String view input was null."; case STRVIEW_ERROR_OUT_OF_RANGE: return "Conversion of string view to value was out of range."; case STRVIEW_ERROR_UNCONSUMED: return "String view was not consumed fully during conversion."; } return "Unknown strview error"; } const error_domain_t ERROR_DOMAIN_STRVIEW_T = { .describe = &describe, .name = "malunal.strview.error" }; static malunal_size_t strview__copy_to_buffer( strview_iptr_t self, malunal_char_t buffer[STRVIEW_NUMBUF_SIZE] ) { malunal_size_t length = strview_length(self); malunal_size_t copied = length >= STRVIEW_NUMBUF_SIZE - 1 ? STRVIEW_NUMBUF_SIZE - 1 : length; if (copied > 0) memcpy(buffer, self->beg, copied); buffer[copied] = '\0'; return copied; } 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 res = { self->beg, self->end }; malunal_size_t len = strview_length(&res); if (start > len) return strview(null, 0); strview_ltrim(&res, start); len = strview_length(&res); if (count > len) return strview(null, 0); strview_rtrim(&res, len - count); return res; } 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 ) { if (self == null || other == null) { if (self < other) return -1; if (self > other) return 1; if (self == other) return 0; } malunal_size_t hlen = strview_length(self); malunal_size_t nlen = strview_length(other); if (hlen < nlen) return -1; if (hlen > nlen) return 1; malunal_size_t smallest = hlen < nlen ? hlen : nlen; return strncmp(self->beg, other->beg, smallest); } 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); if (nlen == 0) return true; 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 (nlen == 0) return true; 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 (nlen == 0) return true; 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) == 0) return true; beg++; hlen--; } return false; } error_t strview_to_int64( strview_iptr_t self, malunal_int64_t* out ) { if (self == null) { if (out != null) *out = 0; return (error_t) { .domain = &ERROR_DOMAIN_STRVIEW_T, .code = STRVIEW_ERROR_NULL_INPUT }; } malunal_char_t buffer[STRVIEW_NUMBUF_SIZE]; malunal_size_t copied = strview__copy_to_buffer(self, buffer); if (copied == 0) { if (out != null) *out = 0; return (error_t) { .domain = &ERROR_DOMAIN_STRVIEW_T, .code = STRVIEW_ERROR_UNCONSUMED }; } malunal_str_t endptr; malunal_int64_t value; errno = 0; value = strtoll(buffer, &endptr, 10); if (out != null) *out = value; if (errno == ERANGE) return (error_t) { .domain = &ERROR_DOMAIN_STRVIEW_T, .code = STRVIEW_ERROR_OUT_OF_RANGE }; return endptr != buffer + copied ? (error_t) { .domain = &ERROR_DOMAIN_STRVIEW_T, .code = STRVIEW_ERROR_UNCONSUMED } : NO_ERROR; } error_t strview_to_double( strview_iptr_t self, malunal_double_t* out ) { if (self == null) { if (out != null) *out = 0.0; return (error_t) { .domain = &ERROR_DOMAIN_STRVIEW_T, .code = STRVIEW_ERROR_NULL_INPUT }; } malunal_char_t buffer[STRVIEW_NUMBUF_SIZE]; malunal_size_t copied = strview__copy_to_buffer(self, buffer); if (copied == 0) { if (out != null) *out = 0.0; return (error_t) { .domain = &ERROR_DOMAIN_STRVIEW_T, .code = STRVIEW_ERROR_UNCONSUMED }; } malunal_str_t endptr; malunal_double_t value; errno = 0; value = strtod(buffer, &endptr); if (out != null) *out = value; if (errno == ERANGE) return (error_t) { .domain = &ERROR_DOMAIN_STRVIEW_T, .code = STRVIEW_ERROR_OUT_OF_RANGE }; return endptr != buffer + copied ? (error_t) { .domain = &ERROR_DOMAIN_STRVIEW_T, .code = STRVIEW_ERROR_UNCONSUMED } : NO_ERROR; }