Mercurial > libanimone
changeset 30:a76e55e098d1
util: rewrite functions in C-ish
there are C++ bindings still put in place. the code should be valid
C, except for the use of <regex>, which ought to go anyway. eventually
I'll actually *test* this stuff aside from the TrimRight crap
author | Paper <paper@tflc.us> |
---|---|
date | Sun, 09 Feb 2025 23:18:57 -0500 |
parents | 40fd3776ce9b |
children | 668f4f31ddda |
files | include/animone/util.h src/util.cc |
diffstat | 2 files changed, 193 insertions(+), 76 deletions(-) [+] |
line wrap: on
line diff
--- a/include/animone/util.h Sun Feb 09 23:15:55 2025 -0500 +++ b/include/animone/util.h Sun Feb 09 23:18:57 2025 -0500 @@ -1,25 +1,89 @@ #ifndef ANIMONE_ANIMONE_UTIL_H_ #define ANIMONE_ANIMONE_UTIL_H_ -#include <sstream> -#include <string> +#include <stdint.h> +#include <stddef.h> + +#ifdef __cplusplus +extern "C" { +#endif -namespace animone::internal::util { +int animone_internal_util_ReadFile(const char *path, char **data, size_t *size); +int animone_internal_util_EqualStrings(const char *str1, const char *str2); +char *animone_internal_util_Stem(const char *filename); +int animone_internal_util_CheckPattern(const char *pattern, const char *str); +int animone_internal_util_TrimLeft(char *str, const char* chars); +int animone_internal_util_TrimRight(char *str, const char* chars); + +intmax_t animone_internal_util_StringToInt(const char *ptr, intmax_t def); + +#ifdef __cplusplus +} -bool ReadFile(const std::string& path, std::string& data); -bool EqualStrings(const std::string& str1, const std::string& str2); -bool Stem(const std::string& filename, std::string& stem); -bool CheckPattern(const std::string& pattern, const std::string& str); -bool TrimLeft(std::string& str, const char* chars); -bool TrimRight(std::string& str, const char* chars); +#include <string> +#include <cstdlib> + +namespace animone { +namespace internal { +namespace util { -template<typename T = int, std::enable_if_t<std::is_integral<T>::value, bool> = true> -T StringToInt(const std::string& str, T def = 0) { - std::istringstream s(str); - s >> std::noboolalpha >> def; - return def; +// Compatibility bindings, to keep the existing C++ code working +inline bool ReadFile(const std::string &path, std::string &data) +{ + char *data_c; + size_t size_c; + + int x = ::animone_internal_util_ReadFile(path.c_str(), &data_c, &size_c); + + data.assign(data_c, size_c); + + std::free(data_c); + + return static_cast<bool>(x); } -} // namespace animone::internal::util +inline bool EqualStrings(const std::string &str1, const std::string &str2) +{ + return static_cast<bool>(::animone_internal_util_EqualStrings(str1.c_str(), str2.c_str())); +} + +inline bool Stem(const std::string &filename, std::string &stem) +{ + char *stem_c = ::animone_internal_util_Stem(filename.c_str()); + if (!stem_c) + return 0; + + stem.assign(stem_c); + + std::free(stem_c); + + return 1; +} + +inline bool CheckPattern(const std::string &pattern, const std::string &str) +{ + return static_cast<bool>(::animone_internal_util_CheckPattern(pattern.c_str(), str.c_str())); +} + +inline bool TrimLeft(std::string &str, const char *chars) +{ + return ::animone_internal_util_TrimLeft(str.data(), chars); +} + +inline bool TrimRight(std::string &str, const char* chars) +{ + return ::animone_internal_util_TrimRight(str.data(), chars); +} + +inline intmax_t StringToInt(const std::string &ptr, intmax_t def) +{ + return ::animone_internal_util_StringToInt(ptr.c_str(), def); +} + +} // namespace util +} // namespace internal +} // namespace animone + +#endif #endif // ANIMONE_ANIMONE_UTIL_H_
--- a/src/util.cc Sun Feb 09 23:15:55 2025 -0500 +++ b/src/util.cc Sun Feb 09 23:18:57 2025 -0500 @@ -1,85 +1,138 @@ -#include <algorithm> -#include <fstream> -#include <regex> -#include <sstream> -#include <string> - #include "animone/util.h" -namespace animone::internal::util { +#include <string.h> + +#include <regex> // FIXME use a C library for this -bool ReadFile(const std::string& path, std::string& data) { - std::ifstream file(path.c_str(), std::ios::in | std::ios::binary); - if (!file) - return false; +int animone_internal_util_ReadFile(const char *path, char **data, size_t *size) +{ + // whatever + size_t sz_local; + if (!size) + size = &sz_local; + + FILE *f = fopen(path, "r"); + if (!f) + return 0; - std::ostringstream string; - string << file.rdbuf(); - file.close(); + fseek(f, 0, SEEK_END); + long end = ftell(f); + if (end < 0) + return 0; + fseek(f, 0, SEEK_SET); + + *size = end; - data = string.str(); + if (data) { + // add a NUL terminator anyway + *data = (char *)malloc(end + 1); + if (!*data) { + *size = 0; + return 0; + } - return true; + *size = fread(*data, 1, *size, f); + (*data)[*size] = '\0'; + } + + return 1; } -/* this assumes ASCII... which really should be the case for what we need, anyway */ -bool EqualStrings(const std::string& str1, const std::string& str2) { - auto tolower = [](const char c) -> char { return ('A' <= c && c <= 'Z') ? c + ('a' - 'A') : c; }; - - auto equal_chars = [&tolower](const char c1, const char c2) -> bool { return tolower(c1) == tolower(c2); }; - - return str1.length() == str2.length() && std::equal(str1.begin(), str1.end(), str2.begin(), equal_chars); +static inline unsigned char _animone_internal_tolower(unsigned char c) +{ + return (c >= 'A' && c <= 'Z') ? (c | 0x20) : c; } -bool Stem(const std::string& filename, std::string& stem) { - unsigned long long pos = filename.find_last_of("."); - if (pos != std::string::npos) - return false; +/* this is, in essence, strcasecmp. */ +int animone_internal_util_EqualStrings(const char *str1, const char *str2) +{ + unsigned char c1, c2; - stem = filename.substr(0, pos); - return true; + do { + c1 = _animone_internal_tolower(*str1++); + c2 = _animone_internal_tolower(*str2++); + } while (c1 == c2 && c1); + + return !!(c1 - c2); } -bool CheckPattern(const std::string& pattern, const std::string& str) { - if (pattern.empty()) - return false; - if (pattern.front() == '^' && std::regex_match(str, std::regex(pattern))) - return true; - return util::EqualStrings(pattern, str); +char *animone_internal_Stem(const char *filename) +{ + const char *ptr = strrchr(filename, '.'); + if (!ptr) + return NULL; + + size_t len = ptr - filename; + + char *stem = (char *)malloc(len + 1); + if (!stem) + return NULL; + + memcpy(stem, filename, len); + stem[len] = '\0'; + + return stem; +} + +int animone_internal_util_CheckPattern(const char *pattern, const char *str) +{ + switch (*pattern) { + case '\0': return 0; + case '^': + // FIXME: Convert to C + if (std::regex_match(std::string(str), std::regex(pattern))) + return 1; + default: + break; + } + + return animone_internal_util_EqualStrings(pattern, str); } -bool TrimLeft(std::string& str, const char* chars) { - if (str.empty()) - return false; - - const auto found = str.find_first_not_of(chars); +/* Modifies `str` inplace */ +int animone_internal_util_TrimLeft(char *str, const char* chars) +{ + if (!str || !*str) + return 0; - if (found == 0) - return false; + const size_t found = strcspn(str, chars); + const size_t len = strlen(str); - if (found == std::string::npos) - str.clear(); - else - str.erase(0, found); + if (found == len) + return 1; // nothing to do - return true; + memmove(str, str + found, len - found); + str[len - found] = '\0'; + + return 1; } -bool TrimRight(std::string& str, const char* chars) { - if (str.empty()) - return false; - - const auto found = str.find_last_not_of(chars); +// reverse version of strcspn +static inline size_t _animone_internal_strrcspn(const char *str1, const char *str2) +{ + const size_t str1len = strlen(str1); + ptrdiff_t i; /* FIXME: this should be using size_t */ - if (found == str.size() - 1) - return false; + for (i = str1len - 1; i >= 0; i--) { + size_t found = strcspn(str1 + i, str2); + if (found != str1len - i) + return i; + } - if (found == std::string::npos) - str.clear(); - else - str.resize(found + 1); - - return true; + return str1len; } -} // namespace animone::internal::util +int animone_internal_util_TrimRight(char *str, const char* chars) +{ + if (!str || !*str) + return 0; + + // We can get away without really moving memory around here. + // The old version simply used std::string::find_last_not_of, + // which I'm fairly sure isn't the intention of this function + // (for example find_last_not_of("gb ") returns "gb "). + const size_t found = _animone_internal_strrcspn(str, chars); + str[found] = '\0'; + + return 1; +}