From 87daa6ae542ecb83f2c890895670eead04ffef47 Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Mon, 20 May 2024 12:51:34 +0000 Subject: [PATCH] Built include --- include/cxutil/cxassert.h | 26 +++---- include/cxutil/cxio.h | 117 ++++++++++++++++++----------- include/cxutil/cxstring.h | 153 +++++++++++++++++++++++++++++++++++--- include/cxutil/cxtips.h | 4 +- 4 files changed, 230 insertions(+), 70 deletions(-) diff --git a/include/cxutil/cxassert.h b/include/cxutil/cxassert.h index a875e79..85793c1 100644 --- a/include/cxutil/cxassert.h +++ b/include/cxutil/cxassert.h @@ -23,7 +23,8 @@ # include -inline void CX_ASSERT_failed(const char* expr, const char* file, int line, const char* message) { +namespace { +void CX_ASSERT_failed(const char* expr, const char* file, int line, const char* message) { fprintf(stderr, "Assert failed: %s\nAt: %s:%d\nMessage: %s", expr, file, line, message); # if defined(_MSC_VER) __debugbreak(); @@ -34,27 +35,20 @@ inline void CX_ASSERT_failed(const char* expr, const char* file, int line, const # endif } -inline void WARNING_failed(const char* expr, const char* file, int line, const char* message) { +void WARNING_failed(const char* expr, const char* file, int line, const char* message) { fprintf(stderr, "Warning: %s\nAt: %s:%d\nMessage: %s", expr, file, line, message); } +} // namespace -# define CX_ASSERT(expr, message) \ - ((expr) ? (void)0 : CX_ASSERT_failed(#expr, __FILE__, __LINE__, message)) +# define CX_ASSERT(expr, message) ((expr) ? (void)0 : CX_ASSERT_failed(#expr, __FILE__, __LINE__, message)) -# define CX_WARNING(expr, message) \ - ((expr) ? (void)0 : WARNING_failed(#expr, __FILE__, __LINE__, message)) +# define CX_WARNING(expr, message) ((expr) ? (void)0 : WARNING_failed(#expr, __FILE__, __LINE__, message)) -# if defined(_MSC_VER) && !defined(_DEBUG) +# if !defined(_DEBUG) || defined(NDEBUG) # undef CX_ASSERT +# undef CX_WARNING # define CX_ASSERT(expr, message) ((void)0) -# elif defined(NDEBUG) -# undef CX_ASSERT -# undef CX_ASSERT_1 -# undef CX_ASSERT_2 -# define CX_ASSERT_1(expr) ((void)0) -# define CX_ASSERT_2(expr, message) ((void)0) -# define GET_MACRO(_1, _2, NAME, ...) NAME -# define CX_ASSERT(...) GET_MACRO(__VA_ARGS__, CX_ASSERT_2, CX_ASSERT_1)(__VA_ARGS__) +# define CX_WARNING(expr, message) ((void)0) # endif -#endif //CXSTRUCTS_SRC_CXASSERT_H_ +#endif //CXSTRUCTS_SRC_CXASSERT_H_ \ No newline at end of file diff --git a/include/cxutil/cxio.h b/include/cxutil/cxio.h index 9e89bd3..865a8c4 100644 --- a/include/cxutil/cxio.h +++ b/include/cxutil/cxio.h @@ -25,13 +25,16 @@ # include // Simple, readable and fast *symmetric* serialization structure with loading -// and saving. Each line is a concatenated list of values and delimiter '|' -// (might be changeable in the future) 13|3.145|This is a string|56| +// and saving. Each line is a concatenated list of values and a separator +// 13|3.145|This is a string|56| + +// I didnt find a way around hard coding the separator... // Using the CXX23 std::print() is about 10% slower + namespace cxstructs { -// inline static constexpr char DELIMITER = '|'; //Not used yet static constexpr int MAX_SECTION_SIZE = 16; +# define NEW_LINE_SUB '\036' //-----------SHARED-----------// namespace { @@ -65,7 +68,6 @@ inline auto io_check_eof(FILE* file) -> bool { fseek(file, currentPos, SEEK_SET); return false; // Not EOF } - //-----------SAVING-----------// // Writes a section header - used like: while(io_load_inside_section()){} inline void io_save_section(FILE* file, const char* value) { @@ -75,26 +77,44 @@ inline void io_save_section(FILE* file, const char* value) { inline void io_save_newline(FILE* file) { fputc('\n', file); } -// Writes a string value to file + +// Writes a string value to file, replacing newlines with the SEPARATOR inline void io_save(FILE* file, const char* value) { - fprintf(file, "%s|", value); + while (*value != '\0') { + if (*value == '\n') { + fputc(NEW_LINE_SUB, file); // Replace newline with SEPARATOR + } else { + fputc(*value, file); // Write the character as is + } + ++value; + } + fputc('\037', file); // Add SEPARATOR at the end } + // Writes an integer or enum property to the file inline void io_save(FILE* file, const int value) { - fprintf(file, "%d|", value); + fprintf(file, "%d\037", value); } -// Writes an boolean to the file + +// Writes a boolean to the file inline void io_save(FILE* file, const bool value) { - fprintf(file, "%d|", value ? 1 : 0); + fprintf(file, "%d\037", value ? 1 : 0); } + // Writes a float to the file inline void io_save(FILE* file, const float value) { - fprintf(file, "%.3f|", value); + fprintf(file, "%.3f\037", value); } -// Writes a three floats to the file + +// Writes three floats to the file - separated by ";" inline void io_save(FILE* file, const float value, const float value2, const float value3) { - fprintf(file, "%.3f|%.3f|%.3F|", value, value2, value3); + fprintf(file, "%.6f;%.6f;%.6f\037", value, value2, value3); +} +// Writes three floats to the file - separated by ";" +inline void io_save(FILE* file, const float value, const float value2) { + fprintf(file, "%.6f;%.6f\037", value, value2); } + // Buffers the given SaveFunc to memory so the file is only written if it // executes successfully. Returns false on error template // SaveFunc(FILE* file) @@ -103,14 +123,12 @@ bool io_save_buffered_write(const char* fileName, const int memoryBufferBytes, S # ifdef _WIN32 FILE* file; - fopen_s(&file,"NUL", "wb"); + fopen_s(&file, "NUL", "wb"); # else FILE* file = fopen("/dev/null", "wb"); # endif // Write to in memory buffer - if (file == nullptr) { - return false; - } + if (file == nullptr) { return false; } auto* buffer = new char[memoryBufferBytes]; std::memset(buffer, 0, memoryBufferBytes); @@ -132,9 +150,9 @@ bool io_save_buffered_write(const char* fileName, const int memoryBufferBytes, S // When successful, open the actual save file and save the data const int dataSize = (int)strlen(buffer); # ifdef _WIN32 - fopen_s(&file,fileName, "wb"); + fopen_s(&file, fileName, "wb"); # else - file = fopen(fileName, "wb"); + file = fopen(fileName, "wb"); # endif if (file == nullptr) { delete[] buffer; @@ -151,21 +169,25 @@ bool io_save_buffered_write(const char* fileName, const int memoryBufferBytes, S delete[] buffer; - if (fclose(file) != 0) { - return false; - } + if (fclose(file) != 0) { return false; } return true; } //-----------LOADING-----------// -// Searches for the next new line but stops at the delimiter if not forced +// Searches for the next new line but stops at the separator if not forced inline void io_load_newline(FILE* file, bool force = false) { char ch; while (fread(&ch, 1, 1, file) == 1) { - if (!force && ch == '|') return; + if (!force && ch == '\037') return; if (ch == '\n') return; } } +inline void io_load_skip_separator(FILE* file) { + char ch; + while (fread(&ch, 1, 1, file) == 1) { + if (ch == '\037') return; + } +} inline bool io_load_inside_section(FILE* file, const char* section) { long currentPos = ftell(file); if (currentPos == -1) return false; // Error - we return false @@ -192,54 +214,65 @@ inline bool io_load_inside_section(FILE* file, const char* section) { if (manual_strncmp(buffer, section, sectionLength) == 0) { io_load_newline(file, false); return true; // Found same section - } else { - io_load_newline(file, false); - return false; // Found new section } + + io_load_newline(file, false); + return false; // Found new section } fseek(file, currentPos, SEEK_SET); return true; // Still inside same section } // include to use -# if defined( _STRING_) || defined(_GLIBCXX_STRING) -inline void io_load(FILE* file, std::string& s, int reserve_amount = 15) { - s.reserve(reserve_amount); +# if defined(_STRING_) || defined(_GLIBCXX_STRING) +inline void io_load(FILE* file, std::string& s) { + //s.reserve(reserve_amount); // Dont need to reserve - string shouldnt allocate below 15 characters char ch; - while (fread(&ch, 1, 1, file) == 1 && ch != '|') { + while (fread(&ch, 1, 1, file) == 1 && ch != '\037') { + if (ch == NEW_LINE_SUB) [[unlikely]] { ch = '\n'; } s.push_back(ch); } + while (ch != '\37' && fread(&ch, 1, 1, file) == 1) {} } # endif -// Load a string property into a user-supplied buffer -inline void io_load(FILE* file, char* buffer, size_t buffer_size) { - size_t count = 0; +// Load a string property into a user-supplied buffer - return bytes written - reads until linesep is found +inline int io_load(FILE* file, char* buffer, size_t buffer_size) { + int count = 0; char ch; - while (count < buffer_size - 1 && fread(&ch, 1, 1, file) == 1 && ch != '|') { + while (count < buffer_size - 1 && fread(&ch, 1, 1, file) == 1 && ch != '\037') { + if (ch == NEW_LINE_SUB) [[unlikely]] { ch = '\n'; } buffer[count++] = ch; } buffer[count] = '\0'; + while (ch != '\037' && fread(&ch, 1, 1, file) == 1) {} + return count; } // Directly load an integer property from the file inline void io_load(FILE* file, int& i) { - fscanf(file, "%d|", &i); + fscanf(file, "%d\037", &i); } -// Directly load a boolean to the file + +// Directly load a boolean from the file inline void io_load(FILE* file, bool& value) { int num = 0; - fscanf(file, "%d|", &num); + fscanf(file, "%d\037", &num); value = num == 1; } + // Directly load a float from the file inline void io_load(FILE* file, float& f) { - fscanf(file, "%f|", &f); + fscanf(file, "%f\037", &f); } -// Directly load a three floats from the file + +// Directly load three floats from the file inline void io_load(FILE* file, float& f, float& f2, float& f3) { - fscanf(file, "%f|%f|%f|", &f, &f2, &f3); + fscanf(file, "%f;%f;%f\037", &f, &f2, &f3); +} +// Directly load three floats from the file +inline void io_load(FILE* file, float& f, float& f2) { + fscanf(file, "%f;%f\037", &f, &f2); +} } - -} // namespace cxstructs # ifdef CX_INCLUDE_TESTS # include diff --git a/include/cxutil/cxstring.h b/include/cxutil/cxstring.h index d04d9ee..cdb9d9c 100644 --- a/include/cxutil/cxstring.h +++ b/include/cxutil/cxstring.h @@ -21,14 +21,16 @@ #ifndef CXSTRUCTS_SRC_CXUTIL_CXSTRING_H_ # define CXSTRUCTS_SRC_CXUTIL_CXSTRING_H_ + # include # include # include +# include namespace cxstructs { // Pads the given string "arg" inside "buff" with the "padSymbol" - optional prefix and suffix -inline void str_pad(char* buff, const int size, const char* arg, const char padSymbol, - const char* prefix = nullptr, const char* suffix = nullptr) { +inline void str_pad(char* buff, const int size, const char* arg, const char padSymbol, const char* prefix = nullptr, + const char* suffix = nullptr) { int currPos = 0; std::memset(buff, 0, size); @@ -40,19 +42,15 @@ inline void str_pad(char* buff, const int size, const char* arg, const char padS currPos += snprintf(buff + currPos, size - currPos, "%s", arg); currPos = currPos > size ? size : currPos; } - if (suffix && currPos < size) { - snprintf(buff + currPos, size - currPos, "%s", suffix); - } + if (suffix && currPos < size) { snprintf(buff + currPos, size - currPos, "%s", suffix); } for (int i = currPos; i < size - 1; i++) { - if (buff[i] == '\0') { - buff[i] = padSymbol; - } + if (buff[i] == '\0') { buff[i] = padSymbol; } } buff[size - 1] = '\0'; } // Measure the length of the given string "arg" using a while loop -inline int str_len(const char* arg) { +constexpr int str_len(const char* arg) { int len = 0; while (*arg) { arg++; @@ -78,6 +76,33 @@ inline bool str_cmp(const char* arg, const char* arg2) { } return *arg == *arg2; // Both should be '\0' if strings are truly equal } +// Case insensitive! - Compares to string with a while loop - stops at max count +inline int str_cmpn_case(const char* s1, const char* s2, int maxCount) { + while (*s1 && *s2 && maxCount > 0) { + const int diff = tolower(*s1) - tolower(*s2); + if (diff != 0) return diff; + ++s1; + ++s2; + maxCount--; + } + return tolower(*s1) - tolower(*s2); +} +// Case insensitive! - Tries to find and return the first occurrence of sequence in string +inline const char* str_substr_case(const char* string, const char* sequence) { + if (!*sequence) return string; + + for (const char* p = string; *p; ++p) { + if (tolower(*p) == tolower(*sequence)) { + const char *h = p, *n = sequence; + while (*h && *n && tolower(*h) == tolower(*n)) { + ++h; + ++n; + } + if (!*n) return p; + } + } + return nullptr; +} // Parses the given string into a int on best effort basis - stops when encountering any non numeric characters inline int str_parse_int(const char* str, const int radix = 10) { if (str == nullptr || *str == '\0') return 0; @@ -134,7 +159,7 @@ inline int64_t str_parse_long(const char* str, const int radix = 10) { } // Parses the given string into a float on best effort basis inline float str_parse_float(const char* str) { - if (str == nullptr || *str == '\0') return 0.0; + if (str == nullptr || *str == '\0') return 0.0F; float result = 0.0; bool negative = false; @@ -163,8 +188,105 @@ inline float str_parse_float(const char* str) { return negative ? -result : result; } +// Parses the given string into a double on best effort basis +inline double str_parse_double(const char* str) { + if (str == nullptr || *str == '\0') return 0.0; + + double result = 0.0; + bool negative = false; + if (*str == '-') { + negative = true; + ++str; + } + + // Parse the integer part + while (*str && *str != '.') { + if (*str < '0' || *str > '9') break; + result = result * 10 + (*str - '0'); + ++str; + } + + // Parse the fractional part + if (*str == '.') { + ++str; + double factor = 0.1; + while (*str && *str >= '0' && *str <= '9') { + result += (*str - '0') * factor; + factor *= 0.1; + ++str; + } + } + + return negative ? -result : result; +} +// Returns the Levenshtein distance for the two given strings +// This penalizes differences in length +// Failure: returns -1 +template +int str_sort_levenshtein(const char* s1, const char* s2) { + if (s1 == nullptr || s2 == nullptr) { + return -1; // Error: string length exceeds maximum allowed length + } + + const size_t len1 = strlen(s1); + const size_t len2 = strlen(s2); + + if (len1 > MAX_LEN || len2 > MAX_LEN) { + return -1; // Error: string length exceeds maximum allowed length + } + + unsigned int d[MAX_LEN + 1][MAX_LEN + 1] = {0}; + + for (unsigned int i = 0; i <= len1; ++i) + d[i][0] = i; + for (unsigned int j = 0; j <= len2; ++j) + d[0][j] = j; + + for (unsigned int i = 1; i <= len1; ++i) { + for (unsigned int j = 1; j <= len2; ++j) { + unsigned int cost = (s1[i - 1] == s2[j - 1]) ? 0 : 1; + unsigned int above = d[i - 1][j] + 1; + unsigned int left = d[i][j - 1] + 1; + unsigned int diag = d[i - 1][j - 1] + cost; + + d[i][j] = above < left ? above : left < diag ? left : diag; + } + } + + return d[len1][len2]; +} +// Case insensitive! - Levenshtein distance +template +int str_sort_levenshtein_case(const char* s1, const char* s2) { + const size_t len1 = strlen(s1); + const size_t len2 = strlen(s2); + + if (len1 > MAX_LEN || len2 > MAX_LEN) { + return -1; // Error: string length exceeds maximum allowed length + } + + unsigned int d[MAX_LEN + 1][MAX_LEN + 1] = {0}; + + for (unsigned int i = 0; i <= len1; ++i) + d[i][0] = i; + for (unsigned int j = 0; j <= len2; ++j) + d[0][j] = j; + + for (unsigned int i = 1; i <= len1; ++i) { + for (unsigned int j = 1; j <= len2; ++j) { + unsigned int cost = (tolower(s1[i - 1]) == tolower(s2[j - 1])) ? 0 : 1; + unsigned int above = d[i - 1][j] + 1; + unsigned int left = d[i][j - 1] + 1; + unsigned int diag = d[i - 1][j - 1] + cost; + + d[i][j] = above < left ? above : left < diag ? left : diag; + } + } + + return d[len1][len2]; +} // string hash function -constexpr auto fnv1a_32(char const* s) noexcept -> uint32_t { +constexpr auto str_hash_fnv1a_32(char const* s) noexcept -> uint32_t { uint32_t hash = 2166136261U; while (*s != 0) { hash ^= (uint32_t)*s++; @@ -187,6 +309,15 @@ struct Fnv1aHash { struct StrEqual { bool operator()(const char* s1, const char* s2) const { return std::strcmp(s1, s2) == 0; } }; +# if defined(_STRING_) || defined(_GLIBCXX_STRING) +// Replaces the string with the string representation of the given number +inline void str_embed_num(std::string& s, float num) { + s.clear(); + char buff[10]; + snprintf(buff, sizeof(buff), "%.6f", num); + s.append(buff); +} +# endif } // namespace cxstructs # ifdef CX_INCLUDE_TESTS diff --git a/include/cxutil/cxtips.h b/include/cxutil/cxtips.h index 0f174c8..f0c57d4 100644 --- a/include/cxutil/cxtips.h +++ b/include/cxutil/cxtips.h @@ -38,9 +38,11 @@ // No Multiple inheritance // No Exceptions - // === Explicit is better than implicit ======================================== +// === Data Oriented Design ======================================== +// Reason about your data and computing method to write fitting code + // === Use 'const' and 'constexpr' ============================================= // Use `const` and `constexpr` where possible to enforce immutability and // compile-time evaluation.