Author | Jamozed <[email protected]> |
Date | 2021-02-21 22:39:52 |
Commit | 9b704eb0a668dd41692320568dd951d4132613c6 |
Parent | 102f6ff42fbee51ffe6a6aa277967b6c519c0d60 |
strconv: Add signed string conversion functions
Diffstat
M | CMakeLists.txt | | | 28 | ++++++++++++++-------------- |
M | README.md | | | 1 | + |
M | src/strconv.h | | | 7 | ++++++- |
A | src/strtoi.c | | | 164 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
M | src/strtou.c | | | 2 | +- |
R | src/test/test_strconv.c -> src/test/test_strtou.c | | | 2 | +- |
6 files changed, 187 insertions, 17 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 8fc5dcd..39f3c47 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -11,19 +11,19 @@ ADD_LIBRARY(lib STATIC ${SOURCES}) LINK_LIBRARIES(lib) -ADD_EXECUTABLE(test_base32 ${PROJECT_SOURCE_DIR}/src/test/test_base32.c) -ADD_EXECUTABLE(test_base64 ${PROJECT_SOURCE_DIR}/src/test/test_base64.c) -ADD_EXECUTABLE(test_crypt ${PROJECT_SOURCE_DIR}/src/test/test_crypt.c) -ADD_EXECUTABLE(test_endian ${PROJECT_SOURCE_DIR}/src/test/test_endian.c) -ADD_EXECUTABLE(test_error ${PROJECT_SOURCE_DIR}/src/test/test_error.c) -ADD_EXECUTABLE(test_optget ${PROJECT_SOURCE_DIR}/src/test/test_optget.c) -ADD_EXECUTABLE(test_strconv ${PROJECT_SOURCE_DIR}/src/test/test_strconv.c) +ADD_EXECUTABLE(test_base32 ${PROJECT_SOURCE_DIR}/src/test/test_base32.c) +ADD_EXECUTABLE(test_base64 ${PROJECT_SOURCE_DIR}/src/test/test_base64.c) +ADD_EXECUTABLE(test_crypt ${PROJECT_SOURCE_DIR}/src/test/test_crypt.c) +ADD_EXECUTABLE(test_endian ${PROJECT_SOURCE_DIR}/src/test/test_endian.c) +ADD_EXECUTABLE(test_error ${PROJECT_SOURCE_DIR}/src/test/test_error.c) +ADD_EXECUTABLE(test_optget ${PROJECT_SOURCE_DIR}/src/test/test_optget.c) +ADD_EXECUTABLE(test_strtou ${PROJECT_SOURCE_DIR}/src/test/test_strtou.c) ENABLE_TESTING() -ADD_TEST(NAME test_base32 COMMAND test_base32) -ADD_TEST(NAME test_base64 COMMAND test_base64) -ADD_TEST(NAME test_crypt COMMAND test_crypt) -ADD_TEST(NAME test_endian COMMAND test_endian) -ADD_TEST(NAME test_error COMMAND test_error) -ADD_TEST(NAME test_optget COMMAND test_optget) -ADD_TEST(NAME test_strconv COMMAND test_strconv) +ADD_TEST(NAME test_base32 COMMAND test_base32) +ADD_TEST(NAME test_base64 COMMAND test_base64) +ADD_TEST(NAME test_crypt COMMAND test_crypt) +ADD_TEST(NAME test_endian COMMAND test_endian) +ADD_TEST(NAME test_error COMMAND test_error) +ADD_TEST(NAME test_optget COMMAND test_optget) +ADD_TEST(NAME test_strtou COMMAND test_strtou) diff --git a/README.md b/README.md index a5cdf19..d8e43a0 100644 --- a/README.md +++ b/README.md @@ -15,6 +15,7 @@ OMKOV lib is a lightweight generic library to be included locally in projects. | error | Error reporting functions | | mode | Parse numeric or symbolic POSIX modes | | optget | Parse command line options | +| strconv | String conversion functions | ## Build Instructions diff --git a/src/strconv.h b/src/strconv.h index 11e91c8..f5c6762 100644 --- a/src/strconv.h +++ b/src/strconv.h @@ -1,4 +1,4 @@ -// strconv.h, version 1.0.0 +// strconv.h, version 1.1.0 // String conversion header file for OMKOV lib // Copyright (C) 2021, Jakob Wakeling // All rights reserved. @@ -35,6 +35,11 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE SOFTWARE. #include <stdint.h> +int8_t strtoi8(const char *nptr, char **endptr, register int base); +int16_t strtoi16(const char *nptr, char **endptr, register int base); +int32_t strtoi32(const char *nptr, char **endptr, register int base); +int64_t strtoi64(const char *nptr, char **endptr, register int base); + uint8_t strtou8(const char *nptr, char **endptr, register int base); uint16_t strtou16(const char *nptr, char **endptr, register int base); uint32_t strtou32(const char *nptr, char **endptr, register int base); diff --git a/src/strtoi.c b/src/strtoi.c new file mode 100644 index 0000000..7951000 --- /dev/null +++ b/src/strtoi.c @@ -0,0 +1,164 @@ +// strtoi.c, version 1.1.0 +// String conversion source file for OMKOV lib +// Copyright (C) 2021, Jakob Wakeling +// All rights reserved. + +/* +OMKOV Permissive Licence, version 1.0 + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal with +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimers. +* Redistributions in binary form must reproduce the above copyright notice, this + list of conditions and the following disclaimers in the documentation and/or + other materials provided with the distribution. +* Neither the names of the copyright holders, nor the names of its contributors + may be used to endorse or promote products derived from this Software without + specific prior written permission. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT +HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE SOFTWARE. +*/ + +#include <ctype.h> +#include <errno.h> +#include <stdbool.h> +#include <stdint.h> + +/* Convert a string to a signed 8-bit integer */ +int8_t strtoi8(const char *nptr, char **endptr, register int base) { + register const char *s = nptr; register uint8_t i = 0, c; bool neg = false; + + for (; isspace(*s); ++s); + + if (*s == '+') { ++s; } else if (*s == '-') { ++s; neg = true; } + + if ((!base || base == 16) && s[0] == '0' && (s[1] == 'x' || s[1] == 'X')) { + s += 2; base = 16; + } + else if (base == 0) { base = s[0] == '0' ? 8 : 10; } + else if (base < 2 || base > 36) { errno = EINVAL; goto end; } + + for (;; ++s) { + if (*s >= '0' && *s <= '9') { c = *s - '0'; } + else if (*s >= 'A' && *s <= 'Z') { c = *s - ('A' - 10); } + else if (*s >= 'a' && *s <= 'z') { c = *s - ('a' - 10); } + else { break; } + + if (c >= base) { break; } + if (i > (neg ? -INT8_MIN : INT8_MAX - c) / base) { + errno = ERANGE; i = neg ? INT8_MIN : INT8_MAX; goto end; + } + + i = i * base + c; + } + +end:; + if (endptr) { *endptr = (char *)s; } return neg ? -i : i; +} + +/* Convert a string to a signed 16-bit integer */ +int16_t strtoi16(const char *nptr, char **endptr, register int base) { + register const char *s = nptr; register uint16_t i = 0, c; bool neg = false; + + for (; isspace(*s); ++s); + + if (*s == '+') { ++s; } else if (*s == '-') { ++s; neg = true; } + + if ((!base || base == 16) && s[0] == '0' && (s[1] == 'x' || s[1] == 'X')) { + s += 2; base = 16; + } + else if (base == 0) { base = s[0] == '0' ? 8 : 10; } + else if (base < 2 || base > 36) { errno = EINVAL; goto end; } + + for (;; ++s) { + if (*s >= '0' && *s <= '9') { c = *s - '0'; } + else if (*s >= 'A' && *s <= 'Z') { c = *s - ('A' - 10); } + else if (*s >= 'a' && *s <= 'z') { c = *s - ('a' - 10); } + else { break; } + + if (c >= base) { break; } + if (i > (neg ? -INT16_MIN : INT16_MAX - c) / base) { + errno = ERANGE; i = neg ? INT16_MIN : INT16_MAX; goto end; + } + + i = i * base + c; + } + +end:; + if (endptr) { *endptr = (char *)s; } return neg ? -i : i; +} + +/* Convert a string to a signed 32-bit integer */ +int32_t strtoi32(const char *nptr, char **endptr, register int base) { + register const char *s = nptr; register uint32_t i = 0, c; bool neg = false; + + for (; isspace(*s); ++s); + + if (*s == '+') { ++s; } else if (*s == '-') { ++s; neg = true; } + + if ((!base || base == 16) && s[0] == '0' && (s[1] == 'x' || s[1] == 'X')) { + s += 2; base = 16; + } + else if (base == 0) { base = s[0] == '0' ? 8 : 10; } + else if (base < 2 || base > 36) { errno = EINVAL; goto end; } + + for (;; ++s) { + if (*s >= '0' && *s <= '9') { c = *s - '0'; } + else if (*s >= 'A' && *s <= 'Z') { c = *s - ('A' - 10); } + else if (*s >= 'a' && *s <= 'z') { c = *s - ('a' - 10); } + else { break; } + + if (c >= base) { break; } + if (i > (neg ? -INT32_MIN : INT32_MAX - c) / base) { + errno = ERANGE; i = neg ? INT32_MIN : INT32_MAX; goto end; + } + + i = i * base + c; + } + +end:; + if (endptr) { *endptr = (char *)s; } return neg ? -i : i; +} + +/* Convert a string to a signed 64-bit integer */ +int64_t strtoi64(const char *nptr, char **endptr, register int base) { + register const char *s = nptr; register uint64_t i = 0, c; bool neg = false; + + for (; isspace(*s); ++s); + + if (*s == '+') { ++s; } else if (*s == '-') { ++s; neg = true; } + + if ((!base || base == 16) && s[0] == '0' && (s[1] == 'x' || s[1] == 'X')) { + s += 2; base = 16; + } + else if (base == 0) { base = s[0] == '0' ? 8 : 10; } + else if (base < 2 || base > 36) { errno = EINVAL; goto end; } + + for (;; ++s) { + if (*s >= '0' && *s <= '9') { c = *s - '0'; } + else if (*s >= 'A' && *s <= 'Z') { c = *s - ('A' - 10); } + else if (*s >= 'a' && *s <= 'z') { c = *s - ('a' - 10); } + else { break; } + + if (c >= base) { break; } + if (i > (neg ? -INT64_MIN : INT64_MAX - c) / base) { + errno = ERANGE; i = neg ? INT64_MIN : INT64_MAX; goto end; + } + + i = i * base + c; + } + +end:; + if (endptr) { *endptr = (char *)s; } return neg ? -i : i; +} diff --git a/src/strtou.c b/src/strtou.c index d320eef..2584dce 100644 --- a/src/strtou.c +++ b/src/strtou.c @@ -1,4 +1,4 @@ -// strtou.c, version 1.0.0 +// strtou.c, version 1.1.0 // String conversion source file for OMKOV lib // Copyright (C) 2021, Jakob Wakeling // All rights reserved. diff --git a/src/test/test_strconv.c b/src/test/test_strtou.c similarity index 99% rename from src/test/test_strconv.c rename to src/test/test_strtou.c index 83d9781..eb9609f 100644 --- a/src/test/test_strconv.c +++ b/src/test/test_strtou.c @@ -1,4 +1,4 @@ -// test_strconv.c +// test_strtou.c // String conversion unit test for OMKOV lib // Copyright (C) 2021, Jakob Wakeling // All rights reserved.