From 287e6e3b8151fac85b4beaeb64f729904551e897 Mon Sep 17 00:00:00 2001 From: vector-of-bool Date: Tue, 5 Apr 2022 00:42:58 +0000 Subject: [PATCH 1/7] Add mlib to the source tree --- src/mlib/mlib.cmake | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 src/mlib/mlib.cmake diff --git a/src/mlib/mlib.cmake b/src/mlib/mlib.cmake new file mode 100644 index 000000000..5dfada555 --- /dev/null +++ b/src/mlib/mlib.cmake @@ -0,0 +1,31 @@ +set (MLIB_SRC_DIR "${CMAKE_CURRENT_LIST_DIR}") + +add_library (_mongo-mlib INTERFACE) +get_filename_component (_inc_dir "${MLIB_SRC_DIR}" DIRECTORY) +target_include_directories (_mongo-mlib INTERFACE "${_inc_dir}") +target_compile_definitions (_mongo-mlib INTERFACE MLIB_USER) +add_library (mongo::mlib ALIAS _mongo-mlib) + +set (_components error path str thread user-check) +set (MLIB_SOURCES) +foreach (comp IN LISTS _components) + set (test_file "${MLIB_SRC_DIR}/${comp}.test.c") + if (EXISTS "${test_file}") + add_executable (mlib.test.${comp} "${test_file}") + target_link_libraries (mlib.test.${comp} PRIVATE mongo::mlib) + add_test (mlib.test.${comp} mlib.test.${comp}) + list (APPEND MLIB_SOURCE "${test_file}") + endif () + list (APPEND MLIB_SOURCES "${MLIB_SRC_DIR}/${comp}.h") +endforeach () + +# This is only here to support export() of mongo::mlib-linked targets. +# Nothing is actually installed +install (TARGETS _mongo-mlib EXPORT _mongo-mlib) +export (EXPORT _mongo-mlib) + +function (target_use_mlib) + foreach (t IN LISTS ARGV) + target_link_libraries("${t}" PRIVATE $) + endforeach () +endfunction () From 751a56dea65c5a93170899362e440f4c7ca4ce5d Mon Sep 17 00:00:00 2001 From: vector-of-bool Date: Thu, 7 Apr 2022 00:07:24 +0000 Subject: [PATCH 2/7] Tweak mlib usage in CMake --- src/mlib/mlib.cmake | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/mlib/mlib.cmake b/src/mlib/mlib.cmake index 5dfada555..9be1d72ba 100644 --- a/src/mlib/mlib.cmake +++ b/src/mlib/mlib.cmake @@ -7,7 +7,6 @@ target_compile_definitions (_mongo-mlib INTERFACE MLIB_USER) add_library (mongo::mlib ALIAS _mongo-mlib) set (_components error path str thread user-check) -set (MLIB_SOURCES) foreach (comp IN LISTS _components) set (test_file "${MLIB_SRC_DIR}/${comp}.test.c") if (EXISTS "${test_file}") @@ -16,7 +15,6 @@ foreach (comp IN LISTS _components) add_test (mlib.test.${comp} mlib.test.${comp}) list (APPEND MLIB_SOURCE "${test_file}") endif () - list (APPEND MLIB_SOURCES "${MLIB_SRC_DIR}/${comp}.h") endforeach () # This is only here to support export() of mongo::mlib-linked targets. @@ -24,7 +22,7 @@ endforeach () install (TARGETS _mongo-mlib EXPORT _mongo-mlib) export (EXPORT _mongo-mlib) -function (target_use_mlib) +function (targets_use_mlib) foreach (t IN LISTS ARGV) target_link_libraries("${t}" PRIVATE $) endforeach () From bb3daca9edb6eb3b3999d8109486a45cf4c5a6a5 Mon Sep 17 00:00:00 2001 From: vector-of-bool Date: Thu, 7 Apr 2022 15:44:58 -0600 Subject: [PATCH 3/7] Add an 'inline definition' function attribute macro --- src/mlib/error.h | 2 +- src/mlib/macros.h | 44 ++++++++++++++++++++++ src/mlib/path.h | 35 ++++++++--------- src/mlib/str.h | 95 ++++++++++++++++++++++++----------------------- src/mlib/thread.h | 5 ++- 5 files changed, 114 insertions(+), 67 deletions(-) create mode 100644 src/mlib/macros.h diff --git a/src/mlib/error.h b/src/mlib/error.h index 7dcb0bede..8fa18699d 100644 --- a/src/mlib/error.h +++ b/src/mlib/error.h @@ -19,7 +19,7 @@ * @return mstr A new string containing the resulting error. Must be freed with * @ref mstr_free(). */ -static inline mstr +mlib_inline_def mstr merror_system_error_string (int errn) { #ifdef _WIN32 diff --git a/src/mlib/macros.h b/src/mlib/macros.h new file mode 100644 index 000000000..48396eb04 --- /dev/null +++ b/src/mlib/macros.h @@ -0,0 +1,44 @@ +#ifndef MLIB_MACROS_H +#define MLIB_MACROS_H + +#include "./user-check.h" + +/** + * @macro mlib_inline Expands to the compiler's "inline" keyword. On C99, just + * 'inline'. On older compilers pre-C99, an implementation-defined "inline" + */ + +// clang-format off +#if defined(__GNUC__) || defined(__clang__) || (defined(_MSC_VER) && _MSC_VER >= 1900) + #define mlib_inline inline +#elif defined(_MSC_VER) + #define mlib_inline __inline +#else + #define mlib_inline _Do_not_know_how_to_say_inline_on_this_compiler +#endif + +/** + * @macro mlib_inline_def + * @brief Declare a function to be inline-defined, but with external linkage. + * + * This has the effect that there will be only one definiton if the final + * program, even if the function is defined in multiple translation units + * (e.g as the result of #inclusion). This mimicks the linkage behavior of + * C++ 'inline' keyword, which is not shared by C99's 'inline'. + */ +#ifdef __INTELLISENSE__ + // Give a pretty definition for intellisense readers + #define mlib_inline_def inline +#elif _WIN32 + // On MSVC/Windows, inline functions are implicitly in COMDAT, even in C + #define mlib_inline_def extern mlib_inline +#else + // On other platforms, declare the symbol "weak" to cause symbol merging, + // and "hidden" to disable the ability for the symbol to be overridden via + // interposition + #define mlib_inline_def extern __attribute__((weak, visibility("hidden"))) +#endif + +// clang-format on + +#endif // MLIB_MACROS_H diff --git a/src/mlib/path.h b/src/mlib/path.h index 2967f4613..628d1eca1 100644 --- a/src/mlib/path.h +++ b/src/mlib/path.h @@ -4,6 +4,7 @@ #include "./user-check.h" #include "./str.h" +#include "./macros.h" #include @@ -34,7 +35,7 @@ typedef enum mpath_format { * @param c A path character * @param f The path format to use */ -static inline bool +mlib_inline_def bool mpath_is_sep (char c, mpath_format f) { if (f == MPATH_WIN32) { @@ -47,7 +48,7 @@ mpath_is_sep (char c, mpath_format f) /** * @brief Obtain the preferred path separator character for the given format */ -static inline char +mlib_inline_def char mpath_preferred_sep (mpath_format f) { if (f == MPATH_WIN32) { @@ -63,7 +64,7 @@ mpath_preferred_sep (mpath_format f) * * @return mstr A new string which must be freed with mstr_free() */ -static inline mstr +mlib_inline_def mstr mpath_current_path () { #if _WIN32 @@ -96,7 +97,7 @@ mpath_current_path () /** * @brief Determine whether the given path string has a trailing path separator */ -static inline bool +mlib_inline_def bool mpath_has_trailing_sep (mstr_view path, mpath_format f) { return path.len && mpath_is_sep (path.data[path.len - 1], f); @@ -105,7 +106,7 @@ mpath_has_trailing_sep (mstr_view path, mpath_format f) /** * @brief Obtain the parent path of the given path. */ -static inline mstr_view +mlib_inline_def mstr_view mpath_parent (mstr_view path, mpath_format f) { if (mpath_has_trailing_sep (path, f)) { @@ -133,7 +134,7 @@ mpath_parent (mstr_view path, mpath_format f) * The returned path will include no directory separators. If the given path * ends with a directory separator, the single-dot '.' path is returned instead. */ -static inline mstr_view +mlib_inline_def mstr_view mpath_filename (mstr_view path, mpath_format f) { if (!path.len) { @@ -163,7 +164,7 @@ mpath_filename (mstr_view path, mpath_format f) * @param f The path format to use * @return mstr A new string resulting from the join */ -static inline mstr +mlib_inline_def mstr mpath_join (mstr_view base, mstr_view suffix, mpath_format f) { if (!base.len) { @@ -194,7 +195,7 @@ mpath_join (mstr_view base, mstr_view suffix, mpath_format f) * For the Windows format, this will return the drive letter, if present. * Otherwise, this will return an empty string. */ -static inline mstr_view +mlib_inline_def mstr_view mpath_root_name (mstr_view path, mpath_format f) { if (f == MPATH_WIN32 && path.len > 1) { @@ -212,7 +213,7 @@ mpath_root_name (mstr_view path, mpath_format f) * * @note This will not include the drive letter of a Win32 path. */ -static inline mstr_view +mlib_inline_def mstr_view mpath_root_directory (mstr_view path, mpath_format f) { mstr_view rname = mpath_root_name (path, f); @@ -229,7 +230,7 @@ mpath_root_directory (mstr_view path, mpath_format f) * * This will include both the root name and the root filepath, if present. */ -static inline mstr_view +mlib_inline_def mstr_view mpath_root_path (mstr_view path, mpath_format f) { mstr_view rname = mpath_root_name (path, f); @@ -243,7 +244,7 @@ mpath_root_path (mstr_view path, mpath_format f) * * @note A Win32 filepath without a drive letter is not absolute! */ -static inline bool +mlib_inline_def bool mpath_is_absolute (mstr_view path, mpath_format f) { if (f == MPATH_WIN32) { @@ -263,7 +264,7 @@ mpath_is_absolute (mstr_view path, mpath_format f) * If the path has a root path, returns the content of the path following that * root path, otherwise returns the same path itself. */ -static inline mstr_view +mlib_inline_def mstr_view mpath_relative_path (mstr_view path, mpath_format f) { mstr_view root = mpath_root_path (path, f); @@ -276,7 +277,7 @@ mpath_relative_path (mstr_view path, mpath_format f) * * @note The return value must be freed with mstr_free() */ -static inline mstr +mlib_inline_def mstr mpath_to_format (mpath_format from, mstr_view path, mpath_format to) { mstr_mut ret = mstr_new (path.len); @@ -296,7 +297,7 @@ mpath_to_format (mpath_format from, mstr_view path, mpath_format to) /** * @brief Determine whether the given path is relative (not absolute) */ -static inline bool +mlib_inline_def bool mpath_is_relative (mstr_view path, mpath_format f) { return !mpath_is_absolute (path, f); @@ -307,7 +308,7 @@ mpath_is_relative (mstr_view path, mpath_format f) * * @note The return value must be freed with mstr_free() */ -static inline mstr +mlib_inline_def mstr mpath_absolute (mstr_view path, mpath_format f); /** @@ -320,7 +321,7 @@ mpath_absolute (mstr_view path, mpath_format f); * @note If `base` is also a relative path, it will also be given to * mpath_absolute() to resolve it. */ -static inline mstr +mlib_inline_def mstr mpath_absolute_from (mstr_view path, mstr_view base, mpath_format f) { mstr_view rname = mpath_root_name (path, f); @@ -363,7 +364,7 @@ mpath_absolute_from (mstr_view path, mstr_view base, mpath_format f) } } -static inline mstr +mlib_inline_def mstr mpath_absolute (mstr_view path, mpath_format f) { if (mpath_is_absolute (path, f)) { diff --git a/src/mlib/str.h b/src/mlib/str.h index 33b4fb623..3898675f2 100644 --- a/src/mlib/str.h +++ b/src/mlib/str.h @@ -2,6 +2,7 @@ #define MONGOCRYPT_STR_PRIVATE_H #include "./user-check.h" +#include "./macros.h" #include #include @@ -141,7 +142,7 @@ typedef struct mstr_mut { * @note The @ref mstr_mut::mstr member MUST eventually be given to * @ref mstr_free(). */ -static inline mstr_mut +mlib_inline_def mstr_mut mstr_new (size_t len) { #ifndef __clang_analyzer__ @@ -160,7 +161,7 @@ mstr_new (size_t len) * @param len The length of the character array, in code units * @return mstr_view A non-owning string. */ -static inline mstr_view +mlib_inline_def mstr_view mstrv_view_data (const char *s, size_t len) { return (mstr_view){.data = s, .len = len}; @@ -173,7 +174,7 @@ mstrv_view_data (const char *s, size_t len) * @param s A pointer to a null-terminated character array * @return mstr_view A view of the pointed-to string */ -static inline mstr_view +mlib_inline_def mstr_view mstrv_view_cstr (const char *s) { return mstrv_view_data (s, strlen (s)); @@ -189,7 +190,7 @@ mstrv_view_cstr (const char *s) * * @note The resulting string will be null-terminated. */ -static inline mstr +mlib_inline_def mstr mstr_copy_data (const char *s, size_t len) { mstr_mut r = mstr_new (len); @@ -203,7 +204,7 @@ mstr_copy_data (const char *s, size_t len) * @param s A pointer to a null-terminated character array * @return mstr A new string copied from the pointed-to string */ -static inline mstr +mlib_inline_def mstr mstr_copy_cstr (const char *s) { return mstr_copy_data (s, strlen (s)); @@ -215,7 +216,7 @@ mstr_copy_cstr (const char *s) * @param s A string view to copy from * @return mstr A new string copied from the given view */ -static inline mstr +mlib_inline_def mstr mstr_copy (mstr_view s) { return mstr_copy_data (s.data, s.len); @@ -226,7 +227,7 @@ mstr_copy (mstr_view s) * * @param s The string to free */ -static inline void +mlib_inline_def void mstr_free (mstr s) { free ((char *) s.data); @@ -239,7 +240,7 @@ mstr_free (mstr s) * @param s The @ref mstr_mut to update * @param new_len The new length of the string */ -static inline void +mlib_inline_def void mstrm_resize (mstr_mut *s, size_t new_len) { if (new_len <= s->len) { @@ -283,7 +284,7 @@ mstrm_resize (mstr_mut *s, size_t new_len) * mstr_assign(&s, convert_to_uppercase(s.view)); * ``` */ -static inline void +mlib_inline_def void mstr_assign (mstr *s, mstr from) { mstr_free (*s); @@ -299,7 +300,7 @@ mstr_assign (mstr *s, mstr from) * @return int The zero-based index of the first instance of `needle` in * `given`, or -1 if no substring is found. */ -static inline int +mlib_inline_def int mstr_find (mstr_view given, mstr_view needle) { const char *const scan_end = given.data + given.len; @@ -335,7 +336,7 @@ mstr_find (mstr_view given, mstr_view needle) * @return int The zero-based index of the last instance of `needle` in * `given`, or -1 if no substring is found. */ -static inline int +mlib_inline_def int mstr_rfind (mstr_view given, mstr_view needle) { if (needle.len > given.len) { @@ -371,7 +372,7 @@ mstr_rfind (mstr_view given, mstr_view needle) * @param insert The string to insert at `at`. * @return mstr A new string that is the result of the splice */ -static inline mstr +mlib_inline_def mstr mstr_splice (mstr_view s, size_t at, size_t del_count, mstr_view insert) { assert (at <= s.len); @@ -395,7 +396,7 @@ mstr_splice (mstr_view s, size_t at, size_t del_count, mstr_view insert) /** * @brief Append the given suffix to the given string */ -static inline mstr +mlib_inline_def mstr mstr_append (mstr_view s, mstr_view suffix) { return mstr_splice (s, s.len, 0, suffix); @@ -404,7 +405,7 @@ mstr_append (mstr_view s, mstr_view suffix) /** * @brief Prepend the given prefix to the given string */ -static inline mstr +mlib_inline_def mstr mstr_prepend (mstr_view s, mstr_view prefix) { return mstr_splice (s, 0, 0, prefix); @@ -418,7 +419,7 @@ mstr_prepend (mstr_view s, mstr_view prefix) * @param infix The string to insert into `s` * @return mstr A new string with `infix` inserted */ -static inline mstr +mlib_inline_def mstr mstr_insert (mstr_view s, size_t at, mstr_view infix) { return mstr_splice (s, at, 0, infix); @@ -432,7 +433,7 @@ mstr_insert (mstr_view s, size_t at, mstr_view infix) * @param count The number of characters to remove * @return mstr A new string with the deletion result. */ -static inline mstr +mlib_inline_def mstr mstr_erase (mstr_view s, size_t at, size_t count) { return mstr_splice (s, at, count, mstrv_view_cstr ("")); @@ -441,7 +442,7 @@ mstr_erase (mstr_view s, size_t at, size_t count) /** * @brief Erase `len` characters from the beginning of the string */ -static inline mstr +mlib_inline_def mstr mstr_remove_prefix (mstr_view s, size_t len) { return mstr_erase (s, 0, len); @@ -450,7 +451,7 @@ mstr_remove_prefix (mstr_view s, size_t len) /** * @brief Erase `len` characters from the end of the string */ -static inline mstr +mlib_inline_def mstr mstr_remove_suffix (mstr_view s, size_t len) { return mstr_erase (s, s.len - len, len); @@ -465,7 +466,7 @@ mstr_remove_suffix (mstr_view s, size_t len) * remaining length. * @return mstr A new string that is a substring of `s` */ -static inline mstr +mlib_inline_def mstr mstr_substr (mstr_view s, size_t at, size_t len) { assert (at <= s.len); @@ -487,7 +488,7 @@ mstr_substr (mstr_view s, size_t at, size_t len) * remaining length. * @return mstr_view A view of `s`. */ -static inline mstr_view +mlib_inline_def mstr_view mstrv_subview (mstr_view s, size_t at, size_t len) { assert (at <= s.len); @@ -502,7 +503,7 @@ mstrv_subview (mstr_view s, size_t at, size_t len) * @brief Obtain a view of another string by removing `len` characters from the * front */ -static inline mstr_view +mlib_inline_def mstr_view mstrv_remove_prefix (mstr_view s, size_t len) { return mstrv_subview (s, len, s.len); @@ -512,7 +513,7 @@ mstrv_remove_prefix (mstr_view s, size_t len) * @brief Obtain a view of another string by removing `len` characters from the * end. */ -static inline mstr_view +mlib_inline_def mstr_view mstrv_remove_suffix (mstr_view s, size_t len) { return mstrv_subview (s, 0, s.len - len); @@ -525,7 +526,7 @@ mstrv_remove_suffix (mstr_view s, size_t len) * @param new_len The new length of the string * @return mstr A new string copied from the beginning of `s` */ -static inline mstr +mlib_inline_def mstr mstr_trunc (mstr_view s, size_t new_len) { assert (new_len <= s.len); @@ -543,7 +544,7 @@ mstr_trunc (mstr_view s, size_t new_len) * * @note If `find` is empty, returns a copy of `string` */ -static inline mstr +mlib_inline_def mstr mstr_replace (const mstr_view string, const mstr_view find, const mstr_view subst) @@ -578,7 +579,7 @@ mstr_replace (const mstr_view string, /** * @brief Determine whether two strings are equivalent. */ -static inline bool +mlib_inline_def bool mstr_eq (mstr_view left, mstr_view right) { if (left.len != right.len) { @@ -588,7 +589,7 @@ mstr_eq (mstr_view left, mstr_view right) } /// Determine whether the given character is an printable ASCII codepoint -static inline bool +mlib_inline_def bool mstr_is_printable (char c) { return (c >= ' ' && c <= '~'); @@ -596,7 +597,7 @@ mstr_is_printable (char c) /// Write the given string to `out`, rendering non-printable characters as hex /// escapes -static inline void +mlib_inline_def void _mstr_write_str_repr_ (FILE *out, mstr_view s) { for (char const *it = s.data; it != s.data + s.len; ++it) { @@ -608,7 +609,7 @@ _mstr_write_str_repr_ (FILE *out, mstr_view s) } } -static inline void +mlib_inline_def void _mstr_assert_fail_ (mstr_view left, const char *predicate, mstr_view right, @@ -623,7 +624,7 @@ _mstr_assert_fail_ (mstr_view left, abort (); } -static inline void +mlib_inline_def void _mstr_assert_ (mstr_view left, mstr_view right, bool (*pred) (mstr_view left, mstr_view right), @@ -660,7 +661,7 @@ _mstr_assert_ (mstr_view left, * @return true If `given` contains at least one occurrence of `needle` * @return false Otherwise */ -static inline bool +mlib_inline_def bool mstr_contains (mstr_view given, mstr_view needle) { return mstr_find (given, needle) >= 0; @@ -669,7 +670,7 @@ mstr_contains (mstr_view given, mstr_view needle) /** * @brief Determine whether `given` starts with `prefix` */ -static inline bool +mlib_inline_def bool mstr_starts_with (mstr_view given, mstr_view prefix) { given = mstrv_subview (given, 0, prefix.len); @@ -679,7 +680,7 @@ mstr_starts_with (mstr_view given, mstr_view prefix) /** * @brief Determine whether `given` ends with `suffix` */ -static inline bool +mlib_inline_def bool mstr_ends_with (mstr_view given, mstr_view suffix) { if (suffix.len > given.len) { @@ -690,70 +691,70 @@ mstr_ends_with (mstr_view given, mstr_view suffix) } /// Compound in-place version of @ref mstr_splice -static inline void +mlib_inline_def void mstr_inplace_splice (mstr *s, size_t at, size_t del_count, mstr_view insert) { mstr_assign (s, mstr_splice (s->view, at, del_count, insert)); } /// Compound in-place version of @ref mstr_append -static inline void +mlib_inline_def void mstr_inplace_append (mstr *s, mstr_view suffix) { mstr_assign (s, mstr_append (s->view, suffix)); } /// Compound in-place version of @ref mstr_prepend -static inline void +mlib_inline_def void mstr_inplace_prepend (mstr *s, mstr_view prefix) { mstr_assign (s, mstr_prepend (s->view, prefix)); } /// Compound in-place version of @ref mstr_insert -static inline void +mlib_inline_def void mstr_inplace_insert (mstr *s, size_t at, mstr_view infix) { mstr_assign (s, mstr_insert (s->view, at, infix)); } /// Compound in-place version of @ref mstr_erase -static inline void +mlib_inline_def void mstr_inplace_erase (mstr *s, size_t at, size_t count) { mstr_assign (s, mstr_erase (s->view, at, count)); } /// Compound in-place version of @ref mstr_remove_prefix -static inline void +mlib_inline_def void mstr_inplace_remove_prefix (mstr *s, size_t len) { mstr_assign (s, mstr_remove_prefix (s->view, len)); } /// Compound in-place version of @ref mstr_remove_suffix -static inline void +mlib_inline_def void mstr_inplace_remove_suffix (mstr *s, size_t len) { mstr_assign (s, mstr_remove_suffix (s->view, len)); } /// Compound in-place version of @ref mstr_substr -static inline void +mlib_inline_def void mstr_inplace_substr (mstr *s, size_t at, size_t count) { mstr_assign (s, mstr_substr (s->view, at, count)); } /// Compound in-place version of @ref mstr_trunc -static inline void +mlib_inline_def void mstr_inplace_trunc (mstr *s, size_t new_len) { mstr_assign (s, mstr_trunc (s->view, new_len)); } /// Compound in-place version of @ref mstr_replace -static inline void +mlib_inline_def void mstr_inplace_replace (mstr *s, mstr_view find, mstr_view subst) { mstr_assign (s, mstr_replace (s->view, find, subst)); @@ -777,7 +778,7 @@ typedef struct mstr_widen_result { * * @note The returned @ref mstr_widen_result::wstring must be given to free() */ -static inline mstr_widen_result +mlib_inline_def mstr_widen_result mstr_win32_widen (mstr_view str) { int length = MultiByteToWideChar ( @@ -810,7 +811,7 @@ typedef struct mstr_narrow_result { * @note The returned @ref mstr_narrow_result::string must be freed with * mstr_free() */ -static inline mstr_narrow_result +mlib_inline_def mstr_narrow_result mstr_win32_narrow (const wchar_t *wstring) { int length = WideCharToMultiByte (CP_UTF8, @@ -855,7 +856,7 @@ struct _mstr_split_iter_ { }; /// Hidden function to advance a string-split iterator -static inline void +mlib_inline_def void _mstr_split_iter_next_ (struct _mstr_split_iter_ *iter) { if (iter->once == 1) { @@ -888,7 +889,7 @@ _mstr_split_iter_next_ (struct _mstr_split_iter_ *iter) } /// init a new split iterator -static inline struct _mstr_split_iter_ +mlib_inline_def struct _mstr_split_iter_ _mstr_split_iter_begin_ (mstr_view str, mstr_view split) { struct _mstr_split_iter_ iter = {.remaining = str, .splitter = split}; @@ -897,7 +898,7 @@ _mstr_split_iter_begin_ (mstr_view str, mstr_view split) } /// Check whether we are done iterating -static inline bool +mlib_inline_def bool _mstr_split_iter_done_ (struct _mstr_split_iter_ *iter) { return iter->state == 2; diff --git a/src/mlib/thread.h b/src/mlib/thread.h index d93fa3450..758b82f28 100644 --- a/src/mlib/thread.h +++ b/src/mlib/thread.h @@ -2,6 +2,7 @@ #define MLIB_THREAD_H #include "./user-check.h" +#include "./macros.h" #ifdef _WIN32 #include "./windows-lean.h" @@ -48,7 +49,7 @@ typedef void (*mlib_init_once_fn_t) (void); /** * An indirection layer for mlib_once on Windows platforms. Do not use directly. */ -static inline BOOL WINAPI +mlib_inline_def BOOL WINAPI _mlib_win32_once_callthru (PINIT_ONCE once, PVOID param, PVOID *ctx) { (void) once; @@ -73,7 +74,7 @@ _mlib_win32_once_callthru (PINIT_ONCE once, PVOID param, PVOID *ctx) * @param fn A callback to execute if the flag is not in the "finished" state * @return true on success, false otherwise */ -static inline bool +mlib_inline_def bool mlib_call_once (mlib_once_flag *flag, mlib_init_once_fn_t fn) { #ifdef _WIN32 From 673bcdd7ab855a3270b68cf731930ce506842bf2 Mon Sep 17 00:00:00 2001 From: vector-of-bool Date: Thu, 7 Apr 2022 01:34:17 +0000 Subject: [PATCH 4/7] Test case to check linker symbol merging --- src/mlib/linkcheck-1.test.c | 7 +++++++ src/mlib/linkcheck-2.test.c | 7 +++++++ src/mlib/linkcheck.test.c | 15 +++++++++++++++ src/mlib/macros.h | 2 +- src/mlib/mlib.cmake | 9 +++++++++ 5 files changed, 39 insertions(+), 1 deletion(-) create mode 100644 src/mlib/linkcheck-1.test.c create mode 100644 src/mlib/linkcheck-2.test.c create mode 100644 src/mlib/linkcheck.test.c diff --git a/src/mlib/linkcheck-1.test.c b/src/mlib/linkcheck-1.test.c new file mode 100644 index 000000000..877065577 --- /dev/null +++ b/src/mlib/linkcheck-1.test.c @@ -0,0 +1,7 @@ +#include "./str.h" + +void * +get_func_addr_1 () +{ + return &mstr_new; +} \ No newline at end of file diff --git a/src/mlib/linkcheck-2.test.c b/src/mlib/linkcheck-2.test.c new file mode 100644 index 000000000..0816bd8eb --- /dev/null +++ b/src/mlib/linkcheck-2.test.c @@ -0,0 +1,7 @@ +#include "./str.h" + +void * +get_func_addr_2 () +{ + return &mstr_new; +} diff --git a/src/mlib/linkcheck.test.c b/src/mlib/linkcheck.test.c new file mode 100644 index 000000000..45ce716eb --- /dev/null +++ b/src/mlib/linkcheck.test.c @@ -0,0 +1,15 @@ +#include + +extern void * +get_func_addr_1 (); +extern void * +get_func_addr_2 (); + +int +main () +{ + if (get_func_addr_1 () != get_func_addr_2 ()) { + fputs ("Multiply-defined symbols were not properly merged.", stderr); + return 1; + } +} diff --git a/src/mlib/macros.h b/src/mlib/macros.h index 48396eb04..ddaefeb71 100644 --- a/src/mlib/macros.h +++ b/src/mlib/macros.h @@ -29,7 +29,7 @@ #ifdef __INTELLISENSE__ // Give a pretty definition for intellisense readers #define mlib_inline_def inline -#elif _WIN32 +#elif defined(_WIN32) // On MSVC/Windows, inline functions are implicitly in COMDAT, even in C #define mlib_inline_def extern mlib_inline #else diff --git a/src/mlib/mlib.cmake b/src/mlib/mlib.cmake index 9be1d72ba..353281720 100644 --- a/src/mlib/mlib.cmake +++ b/src/mlib/mlib.cmake @@ -27,3 +27,12 @@ function (targets_use_mlib) target_link_libraries("${t}" PRIVATE $) endforeach () endfunction () + +add_executable (mlib.test.comdat-link + "${MLIB_SRC_DIR}/linkcheck-1.test.c" + "${MLIB_SRC_DIR}/linkcheck-2.test.c" + "${MLIB_SRC_DIR}/linkcheck.test.c" + ) + +add_test (mlib.test.comdat-link mlib.test.comdat-link) +targets_use_mlib (mlib.test.comdat-link) From bd82d4d42643dee6b05f90815c9608b6c83b8237 Mon Sep 17 00:00:00 2001 From: vector-of-bool Date: Mon, 18 Apr 2022 22:23:11 +0000 Subject: [PATCH 5/7] MinGW lacks WC_ERR_INVALID_CHARS --- src/mlib/str.h | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/src/mlib/str.h b/src/mlib/str.h index 3898675f2..26657743a 100644 --- a/src/mlib/str.h +++ b/src/mlib/str.h @@ -762,6 +762,7 @@ mstr_inplace_replace (mstr *s, mstr_view find, mstr_view subst) #ifdef _WIN32 #include "./windows-lean.h" + /** * @brief The result type of mstr_win32_widen */ @@ -814,8 +815,11 @@ typedef struct mstr_narrow_result { mlib_inline_def mstr_narrow_result mstr_win32_narrow (const wchar_t *wstring) { + // Some older versions of MinGW fail to include the WC_ERR_INVALID_CHARS + // flag, so we will specify it manually: + DWORD wcflags = 0x08; // WC_ERR_INVALID_CHARS int length = WideCharToMultiByte (CP_UTF8, - WC_ERR_INVALID_CHARS, + wcflags, wstring, -1 /* wstring is null-terminated */, NULL, @@ -827,14 +831,8 @@ mstr_win32_narrow (const wchar_t *wstring) .error = GetLastError ()}; } mstr_mut ret = mstr_new ((size_t) length); - int got_len = WideCharToMultiByte (CP_UTF8, - WC_ERR_INVALID_CHARS, - wstring, - -1, - ret.data, - (int) ret.len, - NULL, - NULL); + int got_len = WideCharToMultiByte ( + CP_UTF8, wcflags, wstring, -1, ret.data, (int) ret.len, NULL, NULL); assert (length == got_len); return (mstr_narrow_result){.string = ret.mstr, .error = 0}; } From 54e62b216c935f619f7ae3c09b6568069e86847e Mon Sep 17 00:00:00 2001 From: vector-of-bool Date: Mon, 18 Apr 2022 22:57:49 +0000 Subject: [PATCH 6/7] Include mlib.cmake to import the library --- CMakeLists.txt | 18 +----------------- 1 file changed, 1 insertion(+), 17 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 61523edf5..efa6bf188 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -164,13 +164,7 @@ configure_file ( # Define the mlib target, which is private and header-only. It is not exported # nor are its headers installed. -add_library (_mongo-mlib INTERFACE) -add_library (mongo::mlib ALIAS _mongo-mlib) -set_property( - TARGET _mongo-mlib - APPEND PROPERTY INTERFACE_COMPILE_DEFINITIONS - MLIB_USER - ) +include (./src/mlib/mlib.cmake) # kms-message add_subdirectory (kms-message) @@ -405,12 +399,6 @@ add_test ( WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} ) -foreach (test IN ITEMS path str) - add_executable (mlib.${test}.test src/mlib/${test}.test.c) - add_test (mlib.${test} mlib.${test}.test) - target_link_libraries (mlib.${test}.test PRIVATE mongo::mlib) -endforeach () - # Exclude example-state-machine since it requires native crypto. if (NOT MONGOCRYPT_CRYPTO STREQUAL none) # Define example-state-machine @@ -453,10 +441,6 @@ install ( INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} ) -# This export set is not installed, and is only to allow export() of the mlib-using targets -install (TARGETS _mongo-mlib EXPORT _exports_for_export) -export (EXPORT _exports_for_export) - install ( FILES ${MONGOCRYPT_PUBLIC_HEADERS} From 8bef7728e031aaea643b23f3f397fb7fba9248fa Mon Sep 17 00:00:00 2001 From: vector-of-bool Date: Mon, 18 Apr 2022 18:10:14 -0600 Subject: [PATCH 7/7] Use WeakExternals on Windows --- src/mlib/macros.h | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/mlib/macros.h b/src/mlib/macros.h index ddaefeb71..c6f3d1c65 100644 --- a/src/mlib/macros.h +++ b/src/mlib/macros.h @@ -30,8 +30,12 @@ // Give a pretty definition for intellisense readers #define mlib_inline_def inline #elif defined(_WIN32) - // On MSVC/Windows, inline functions are implicitly in COMDAT, even in C - #define mlib_inline_def extern mlib_inline + #ifdef __GNUC__ + #define mlib_inline_def extern __attribute__((weak, visibility("hidden"))) + #else + // On MSVC/Windows, inline functions are implicitly in COMDAT, even in C + #define mlib_inline_def extern mlib_inline + #endif #else // On other platforms, declare the symbol "weak" to cause symbol merging, // and "hidden" to disable the ability for the symbol to be overridden via