Conversion functions
This commit is contained in:
@@ -4,7 +4,7 @@
|
|||||||
* @author John Christman (sorakatadzuma@gmail.com)
|
* @author John Christman (sorakatadzuma@gmail.com)
|
||||||
* @copyright Malunal Studios, LLC.
|
* @copyright Malunal Studios, LLC.
|
||||||
*/
|
*/
|
||||||
#include "malunal/type.h"
|
#include "malunal/types/error.h"
|
||||||
|
|
||||||
#ifndef MALUNAL_STRVIEW_HEADER
|
#ifndef MALUNAL_STRVIEW_HEADER
|
||||||
#define MALUNAL_STRVIEW_HEADER
|
#define MALUNAL_STRVIEW_HEADER
|
||||||
@@ -18,6 +18,14 @@
|
|||||||
*/
|
*/
|
||||||
#define STRVIEW_NPOS ((malunal_size_t)-1)
|
#define STRVIEW_NPOS ((malunal_size_t)-1)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Imports the @c strview_t error domain for error checking.
|
||||||
|
* @details The @c strview_t error domain is specific for string views to
|
||||||
|
* indicate an issue with functions relating to string views.
|
||||||
|
*/
|
||||||
|
extern
|
||||||
|
const error_domain_t
|
||||||
|
ERROR_DOMAIN_STRVIEW_T;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @struct strview
|
* @struct strview
|
||||||
@@ -43,6 +51,20 @@ define_struct(strview) {
|
|||||||
malunal_cstr_t end;
|
malunal_cstr_t end;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Defines the set of errors that may be triggered by a string view.
|
||||||
|
* @details This are used in conjunction with a domain to indicate an error
|
||||||
|
* that comes specifically from a string view or its functions and
|
||||||
|
* what that error means.
|
||||||
|
*/
|
||||||
|
typedef enum {
|
||||||
|
STRVIEW_ERROR_NULL_INPUT,
|
||||||
|
STRVIEW_ERROR_OUT_OF_RANGE,
|
||||||
|
STRVIEW_ERROR_UNCONSUMED,
|
||||||
|
} strview_error_t;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Creates a string view from the provided @c data and @c length.
|
* @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 data A pointer to the data of the string view.
|
||||||
@@ -185,4 +207,28 @@ strview_contains(
|
|||||||
strview_iptr_t other
|
strview_iptr_t other
|
||||||
);
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Converts the string view to an @c long @c long, ala @c strtoll.
|
||||||
|
* @param self The string view to convert.
|
||||||
|
* @param out If not null, populated with the converted value.
|
||||||
|
* @returns An error if the conversion could not be performed.
|
||||||
|
*/
|
||||||
|
error_t
|
||||||
|
strview_to_int64(
|
||||||
|
strview_iptr_t self,
|
||||||
|
malunal_int64_t* out
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Converts the string view to a @c float, ala @c strtod.
|
||||||
|
* @param self The string view to convert.
|
||||||
|
* @param out If not null, populated with the converted value.
|
||||||
|
* @returns An error if the conversion could not be performed.
|
||||||
|
*/
|
||||||
|
error_t
|
||||||
|
strview_to_double(
|
||||||
|
strview_iptr_t self,
|
||||||
|
malunal_double_t* out
|
||||||
|
);
|
||||||
|
|
||||||
#endif /* MALUNAL_STRVIEW_HEADER */
|
#endif /* MALUNAL_STRVIEW_HEADER */
|
||||||
|
|||||||
@@ -1,6 +1,47 @@
|
|||||||
|
#include <errno.h>
|
||||||
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include "malunal/strview.h"
|
#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_t
|
||||||
strview(
|
strview(
|
||||||
@@ -178,3 +219,97 @@ strview_contains(
|
|||||||
|
|
||||||
return false;
|
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;
|
||||||
|
}
|
||||||
|
|||||||
+195
-1
@@ -366,6 +366,201 @@ MICROTEST(strview_contains, false_when_other_longer_than_self) {
|
|||||||
MICROTEST_EXPECT_FALSE(strview_contains(&self, &other));
|
MICROTEST_EXPECT_FALSE(strview_contains(&self, &other));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------------------------------------
|
||||||
|
* strview_to_int64()
|
||||||
|
* ---------------------------------------------------------------------- */
|
||||||
|
|
||||||
|
MICROTEST(strview_to_int64, positive_integer_succeeds) {
|
||||||
|
strview_t view = strview_from_cstr("12345");
|
||||||
|
malunal_int64_t out = 0;
|
||||||
|
error_t err = strview_to_int64(&view, &out);
|
||||||
|
|
||||||
|
MICROTEST_EXPECT_NULL(err.domain);
|
||||||
|
MICROTEST_EXPECT_EQ(out, (malunal_int64_t)12345);
|
||||||
|
}
|
||||||
|
|
||||||
|
MICROTEST(strview_to_int64, negative_integer_succeeds) {
|
||||||
|
strview_t view = strview_from_cstr("-42");
|
||||||
|
malunal_int64_t out = 0;
|
||||||
|
error_t err = strview_to_int64(&view, &out);
|
||||||
|
|
||||||
|
MICROTEST_EXPECT_NULL(err.domain);
|
||||||
|
MICROTEST_EXPECT_EQ(out, (malunal_int64_t)-42);
|
||||||
|
}
|
||||||
|
|
||||||
|
MICROTEST(strview_to_int64, zero_succeeds) {
|
||||||
|
strview_t view = strview_from_cstr("0");
|
||||||
|
malunal_int64_t out = -1;
|
||||||
|
error_t err = strview_to_int64(&view, &out);
|
||||||
|
|
||||||
|
MICROTEST_EXPECT_NULL(err.domain);
|
||||||
|
MICROTEST_EXPECT_EQ(out, (malunal_int64_t)0);
|
||||||
|
}
|
||||||
|
|
||||||
|
MICROTEST(strview_to_int64, succeeds_on_subview_not_ending_at_buffer_end) {
|
||||||
|
malunal_cstr_t data = "12345 and then some trailing text";
|
||||||
|
strview_t view = strview(data, 5);
|
||||||
|
malunal_int64_t out = 0;
|
||||||
|
error_t err = strview_to_int64(&view, &out);
|
||||||
|
|
||||||
|
MICROTEST_EXPECT_NULL(err.domain);
|
||||||
|
MICROTEST_EXPECT_EQ(out, (malunal_int64_t)12345);
|
||||||
|
}
|
||||||
|
|
||||||
|
MICROTEST(strview_to_int64, trailing_garbage_reports_unconsumed) {
|
||||||
|
strview_t view = strview_from_cstr("123abc");
|
||||||
|
malunal_int64_t out = 0;
|
||||||
|
error_t err = strview_to_int64(&view, &out);
|
||||||
|
|
||||||
|
MICROTEST_EXPECT_NOT_NULL(err.domain);
|
||||||
|
MICROTEST_EXPECT_EQ(err.code, (malunal_int32_t)STRVIEW_ERROR_UNCONSUMED);
|
||||||
|
}
|
||||||
|
|
||||||
|
MICROTEST(strview_to_int64, non_numeric_reports_unconsumed) {
|
||||||
|
strview_t view = strview_from_cstr("abc");
|
||||||
|
malunal_int64_t out = 0;
|
||||||
|
error_t err = strview_to_int64(&view, &out);
|
||||||
|
|
||||||
|
MICROTEST_EXPECT_NOT_NULL(err.domain);
|
||||||
|
MICROTEST_EXPECT_EQ(err.code, (malunal_int32_t)STRVIEW_ERROR_UNCONSUMED);
|
||||||
|
}
|
||||||
|
|
||||||
|
MICROTEST(strview_to_int64, empty_view_reports_unconsumed) {
|
||||||
|
strview_t view = strview_from_cstr("");
|
||||||
|
malunal_int64_t out = 0;
|
||||||
|
error_t err = strview_to_int64(&view, &out);
|
||||||
|
|
||||||
|
MICROTEST_EXPECT_NOT_NULL(err.domain);
|
||||||
|
MICROTEST_EXPECT_EQ(err.code, (malunal_int32_t)STRVIEW_ERROR_UNCONSUMED);
|
||||||
|
}
|
||||||
|
|
||||||
|
MICROTEST(strview_to_int64, null_self_reports_null_input) {
|
||||||
|
malunal_int64_t out = 99;
|
||||||
|
error_t err = strview_to_int64(null, &out);
|
||||||
|
|
||||||
|
MICROTEST_EXPECT_NOT_NULL(err.domain);
|
||||||
|
MICROTEST_EXPECT_EQ(err.code, (malunal_int32_t)STRVIEW_ERROR_NULL_INPUT);
|
||||||
|
MICROTEST_EXPECT_EQ(out, (malunal_int64_t)0);
|
||||||
|
}
|
||||||
|
|
||||||
|
MICROTEST(strview_to_int64, overflow_reports_out_of_range) {
|
||||||
|
strview_t view = strview_from_cstr("99999999999999999999999");
|
||||||
|
malunal_int64_t out = 0;
|
||||||
|
error_t err = strview_to_int64(&view, &out);
|
||||||
|
|
||||||
|
MICROTEST_EXPECT_NOT_NULL(err.domain);
|
||||||
|
MICROTEST_EXPECT_EQ(err.code, (malunal_int32_t)STRVIEW_ERROR_OUT_OF_RANGE);
|
||||||
|
}
|
||||||
|
|
||||||
|
MICROTEST(strview_to_int64, underflow_reports_out_of_range) {
|
||||||
|
strview_t view = strview_from_cstr("-99999999999999999999999");
|
||||||
|
malunal_int64_t out = 0;
|
||||||
|
error_t err = strview_to_int64(&view, &out);
|
||||||
|
|
||||||
|
MICROTEST_EXPECT_NOT_NULL(err.domain);
|
||||||
|
MICROTEST_EXPECT_EQ(err.code, (malunal_int32_t)STRVIEW_ERROR_OUT_OF_RANGE);
|
||||||
|
}
|
||||||
|
|
||||||
|
MICROTEST(strview_to_int64, null_out_pointer_does_not_crash) {
|
||||||
|
strview_t view = strview_from_cstr("12345");
|
||||||
|
error_t err = strview_to_int64(&view, null);
|
||||||
|
|
||||||
|
MICROTEST_EXPECT_NULL(err.domain);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------------------------------------
|
||||||
|
* strview_to_double()
|
||||||
|
* ---------------------------------------------------------------------- */
|
||||||
|
|
||||||
|
MICROTEST(strview_to_double, positive_decimal_succeeds) {
|
||||||
|
strview_t view = strview_from_cstr("3.14");
|
||||||
|
malunal_double_t out = 0.0;
|
||||||
|
error_t err = strview_to_double(&view, &out);
|
||||||
|
|
||||||
|
MICROTEST_EXPECT_NULL(err.domain);
|
||||||
|
MICROTEST_EXPECT_TRUE(out > 3.139 && out < 3.141);
|
||||||
|
}
|
||||||
|
|
||||||
|
MICROTEST(strview_to_double, negative_decimal_succeeds) {
|
||||||
|
strview_t view = strview_from_cstr("-2.5");
|
||||||
|
malunal_double_t out = 0.0;
|
||||||
|
error_t err = strview_to_double(&view, &out);
|
||||||
|
|
||||||
|
MICROTEST_EXPECT_NULL(err.domain);
|
||||||
|
MICROTEST_EXPECT_TRUE(out > -2.501 && out < -2.499);
|
||||||
|
}
|
||||||
|
|
||||||
|
MICROTEST(strview_to_double, integer_like_value_succeeds) {
|
||||||
|
strview_t view = strview_from_cstr("42");
|
||||||
|
malunal_double_t out = 0.0;
|
||||||
|
error_t err = strview_to_double(&view, &out);
|
||||||
|
|
||||||
|
MICROTEST_EXPECT_NULL(err.domain);
|
||||||
|
MICROTEST_EXPECT_TRUE(out > 41.999 && out < 42.001);
|
||||||
|
}
|
||||||
|
|
||||||
|
MICROTEST(strview_to_double, succeeds_on_subview_not_ending_at_buffer_end) {
|
||||||
|
malunal_cstr_t data = "3.14 and then some trailing text";
|
||||||
|
strview_t view = strview(data, 4);
|
||||||
|
malunal_double_t out = 0.0;
|
||||||
|
error_t err = strview_to_double(&view, &out);
|
||||||
|
|
||||||
|
MICROTEST_EXPECT_NULL(err.domain);
|
||||||
|
MICROTEST_EXPECT_TRUE(out > 3.139 && out < 3.141);
|
||||||
|
}
|
||||||
|
|
||||||
|
MICROTEST(strview_to_double, trailing_garbage_reports_unconsumed) {
|
||||||
|
strview_t view = strview_from_cstr("3.14xyz");
|
||||||
|
malunal_double_t out = 0.0;
|
||||||
|
error_t err = strview_to_double(&view, &out);
|
||||||
|
|
||||||
|
MICROTEST_EXPECT_NOT_NULL(err.domain);
|
||||||
|
MICROTEST_EXPECT_EQ(err.code, (malunal_int32_t)STRVIEW_ERROR_UNCONSUMED);
|
||||||
|
}
|
||||||
|
|
||||||
|
MICROTEST(strview_to_double, non_numeric_reports_unconsumed) {
|
||||||
|
strview_t view = strview_from_cstr("abc");
|
||||||
|
malunal_double_t out = 0.0;
|
||||||
|
error_t err = strview_to_double(&view, &out);
|
||||||
|
|
||||||
|
MICROTEST_EXPECT_NOT_NULL(err.domain);
|
||||||
|
MICROTEST_EXPECT_EQ(err.code, (malunal_int32_t)STRVIEW_ERROR_UNCONSUMED);
|
||||||
|
}
|
||||||
|
|
||||||
|
MICROTEST(strview_to_double, empty_view_reports_unconsumed) {
|
||||||
|
strview_t view = strview_from_cstr("");
|
||||||
|
malunal_double_t out = 0.0;
|
||||||
|
error_t err = strview_to_double(&view, &out);
|
||||||
|
|
||||||
|
MICROTEST_EXPECT_NOT_NULL(err.domain);
|
||||||
|
MICROTEST_EXPECT_EQ(err.code, (malunal_int32_t)STRVIEW_ERROR_UNCONSUMED);
|
||||||
|
}
|
||||||
|
|
||||||
|
MICROTEST(strview_to_double, null_self_reports_null_input) {
|
||||||
|
malunal_double_t out = 99.0;
|
||||||
|
error_t err = strview_to_double(null, &out);
|
||||||
|
|
||||||
|
MICROTEST_EXPECT_NOT_NULL(err.domain);
|
||||||
|
MICROTEST_EXPECT_EQ(err.code, (malunal_int32_t)STRVIEW_ERROR_NULL_INPUT);
|
||||||
|
MICROTEST_EXPECT_TRUE(out == 0.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
MICROTEST(strview_to_double, overflow_reports_out_of_range) {
|
||||||
|
strview_t view = strview_from_cstr("1e400");
|
||||||
|
malunal_double_t out = 0.0;
|
||||||
|
error_t err = strview_to_double(&view, &out);
|
||||||
|
|
||||||
|
MICROTEST_EXPECT_NOT_NULL(err.domain);
|
||||||
|
MICROTEST_EXPECT_EQ(err.code, (malunal_int32_t)STRVIEW_ERROR_OUT_OF_RANGE);
|
||||||
|
}
|
||||||
|
|
||||||
|
MICROTEST(strview_to_double, null_out_pointer_does_not_crash) {
|
||||||
|
strview_t view = strview_from_cstr("3.14");
|
||||||
|
error_t err = strview_to_double(&view, null);
|
||||||
|
|
||||||
|
MICROTEST_EXPECT_NULL(err.domain);
|
||||||
|
}
|
||||||
|
|
||||||
/* -------------------------------------------------------------------------
|
/* -------------------------------------------------------------------------
|
||||||
* STRVIEW_NPOS
|
* STRVIEW_NPOS
|
||||||
* ---------------------------------------------------------------------- */
|
* ---------------------------------------------------------------------- */
|
||||||
@@ -375,4 +570,3 @@ MICROTEST(strview_npos, is_max_size_value) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
MICROTEST_MAIN()
|
MICROTEST_MAIN()
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user