2026-09-12 10:08:59 -05:00
|
|
|
#include <errno.h>
|
|
|
|
|
#include <stdlib.h>
|
2026-09-12 08:27:01 -05:00
|
|
|
#include <string.h>
|
|
|
|
|
#include "malunal/strview.h"
|
|
|
|
|
|
2026-09-12 10:08:59 -05:00
|
|
|
#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;
|
|
|
|
|
}
|
|
|
|
|
|
2026-09-12 08:27:01 -05:00
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
}
|
2026-09-12 10:08:59 -05:00
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
}
|