diff --git a/src/libstore/c/nix_api_store.cc b/src/libstore/c/nix_api_store.cc index 199f5526a..93e1626a1 100644 --- a/src/libstore/c/nix_api_store.cc +++ b/src/libstore/c/nix_api_store.cc @@ -56,26 +56,24 @@ void nix_store_free(Store * store) delete store; } -nix_err nix_store_get_uri(nix_c_context * context, Store * store, char * dest, unsigned int n) +nix_err nix_store_get_uri(nix_c_context * context, Store * store, void * callback, void * user_data) { if (context) context->last_err_code = NIX_OK; try { auto res = store->ptr->getUri(); - return nix_export_std_string(context, res, dest, n); + return call_nix_observe_string(res, callback, user_data); } NIXC_CATCH_ERRS } -nix_err nix_store_get_version(nix_c_context * context, Store * store, char * dest, unsigned int n) +nix_err nix_store_get_version(nix_c_context * context, Store * store, void * callback, void * user_data) { if (context) context->last_err_code = NIX_OK; try { auto res = store->ptr->getVersion(); - if (!res) - res = ""; - return nix_export_std_string(context, *res, dest, n); + return call_nix_observe_string(res.value_or(""), callback, user_data); } NIXC_CATCH_ERRS } diff --git a/src/libstore/c/nix_api_store.h b/src/libstore/c/nix_api_store.h index 28544fa90..25175de44 100644 --- a/src/libstore/c/nix_api_store.h +++ b/src/libstore/c/nix_api_store.h @@ -71,11 +71,12 @@ void nix_store_free(Store * store); * @brief get the URI of a nix store * @param[out] context Optional, stores error information * @param[in] store nix store reference - * @param[out] dest The allocated area to write the string to. - * @param[in] n Maximum size of the returned string. + * @param[in] callback Called with the URI. + * @param[in] user_data optional, arbitrary data, passed to the callback when it's called. + * @see nix_observe_string * @return error code, NIX_OK on success. */ -nix_err nix_store_get_uri(nix_c_context * context, Store * store, char * dest, unsigned int n); +nix_err nix_store_get_uri(nix_c_context * context, Store * store, void * callback, void * user_data); // returns: owned StorePath* /** @@ -130,11 +131,12 @@ nix_err nix_store_realise( * If the store doesn't have a version (like the dummy store), returns an empty string. * @param[out] context Optional, stores error information * @param[in] store nix store reference - * @param[out] dest The allocated area to write the string to. - * @param[in] n Maximum size of the returned string. + * @param[in] callback Called with the version. + * @param[in] user_data optional, arbitrary data, passed to the callback when it's called. + * @see nix_observe_string * @return error code, NIX_OK on success. */ -nix_err nix_store_get_version(nix_c_context *, Store * store, char * dest, unsigned int n); +nix_err nix_store_get_version(nix_c_context * context, Store * store, void * callback, void * user_data); // cffi end #ifdef __cplusplus diff --git a/src/libutil/c/nix_api_util.cc b/src/libutil/c/nix_api_util.cc index 100e3b21d..ed542059d 100644 --- a/src/libutil/c/nix_api_util.cc +++ b/src/libutil/c/nix_api_util.cc @@ -63,16 +63,17 @@ const char * nix_version_get() } // Implementations -nix_err nix_setting_get(nix_c_context * context, const char * key, char * value, int n) + +nix_err nix_setting_get(nix_c_context * context, const char * key, void * callback, void * user_data) { if (context) context->last_err_code = NIX_OK; try { std::map settings; nix::globalConfig.getSettings(settings); - if (settings.contains(key)) - return nix_export_std_string(context, settings[key].value, value, n); - else { + if (settings.contains(key)) { + return call_nix_observe_string(settings[key].value, callback, user_data); + } else { return nix_set_err_msg(context, NIX_ERR_KEY, "Setting not found"); } } @@ -114,24 +115,24 @@ const char * nix_err_msg(nix_c_context * context, const nix_c_context * read_con return nullptr; } -nix_err nix_err_name(nix_c_context * context, const nix_c_context * read_context, char * value, int n) +nix_err nix_err_name(nix_c_context * context, const nix_c_context * read_context, void * callback, void * user_data) { if (context) context->last_err_code = NIX_OK; if (read_context->last_err_code != NIX_ERR_NIX_ERROR) { return nix_set_err_msg(context, NIX_ERR_UNKNOWN, "Last error was not a nix error"); } - return nix_export_std_string(context, read_context->name, value, n); + return call_nix_observe_string(read_context->name, callback, user_data); } -nix_err nix_err_info_msg(nix_c_context * context, const nix_c_context * read_context, char * value, int n) +nix_err nix_err_info_msg(nix_c_context * context, const nix_c_context * read_context, void * callback, void * user_data) { if (context) context->last_err_code = NIX_OK; if (read_context->last_err_code != NIX_ERR_NIX_ERROR) { return nix_set_err_msg(context, NIX_ERR_UNKNOWN, "Last error was not a nix error"); } - return nix_export_std_string(context, read_context->info->msg.str(), value, n); + return call_nix_observe_string(read_context->info->msg.str(), callback, user_data); } nix_err nix_err_code(const nix_c_context * read_context) @@ -140,12 +141,8 @@ nix_err nix_err_code(const nix_c_context * read_context) } // internal -nix_err nix_export_std_string(nix_c_context * context, const std::string_view str, char * dest, unsigned int n) +nix_err call_nix_observe_string(const std::string str, void * callback, void * user_data) { - size_t i = str.copy(dest, n - 1); - dest[i] = 0; - if (i == n - 1) { - return nix_set_err_msg(context, NIX_ERR_OVERFLOW, "Provided buffer too short"); - } else - return NIX_OK; + ((nix_observe_string) callback)(str.c_str(), str.size(), user_data); + return NIX_OK; } diff --git a/src/libutil/c/nix_api_util.h b/src/libutil/c/nix_api_util.h index c288654fd..fc6dc8655 100644 --- a/src/libutil/c/nix_api_util.h +++ b/src/libutil/c/nix_api_util.h @@ -119,6 +119,15 @@ typedef int nix_err; */ typedef struct nix_c_context nix_c_context; +/** + * @brief Called to get the value of a string owned by Nix. + * + * @param[in] start the string to copy. + * @param[in] n the string length. + * @param[in] user_data optional, arbitrary data, passed to the nix_observe_string when it's called. + */ +typedef void (*nix_observe_string)(const char * start, unsigned int n, void * user_data); + // Function prototypes /** @@ -160,14 +169,13 @@ nix_err nix_libutil_init(nix_c_context * context); * * @param[out] context optional, Stores error information * @param[in] key The key of the setting to retrieve. - * @param[out] value A pointer to a buffer where the value of the setting will - * be stored. - * @param[in] n The size of the buffer pointed to by value. - * @return NIX_ERR_KEY if the setting is unknown, NIX_ERR_OVERFLOW if the - * provided buffer is too short, or NIX_OK if the setting was retrieved + * @param[in] callback Called with the setting value. + * @param[in] user_data optional, arbitrary data, passed to the callback when it's called. + * @see nix_observe_string + * @return NIX_ERR_KEY if the setting is unknown, or NIX_OK if the setting was retrieved * successfully. */ -nix_err nix_setting_get(nix_c_context * context, const char * key, char * value, int n); +nix_err nix_setting_get(nix_c_context * context, const char * key, void * callback, void * user_data); /** * @brief Sets a setting in the nix global configuration. @@ -227,12 +235,14 @@ const char * nix_err_msg(nix_c_context * context, const nix_c_context * ctx, uns * * @param[out] context optional, the context to store errors in if this function * fails - * @param[in] read_context the context to retrieve the error message from - * @param[out] value The allocated area to write the error string to. - * @param[in] n Maximum size of the returned string. + * @param[in] read_context the context to retrieve the error message from. + * @param[in] callback Called with the error message. + * @param[in] user_data optional, arbitrary data, passed to the callback when it's called. + * @see nix_observe_string * @return NIX_OK if there were no errors, an error code otherwise. */ -nix_err nix_err_info_msg(nix_c_context * context, const nix_c_context * read_context, char * value, int n); +nix_err +nix_err_info_msg(nix_c_context * context, const nix_c_context * read_context, void * callback, void * user_data); /** * @brief Retrieves the error name from a context. @@ -245,11 +255,12 @@ nix_err nix_err_info_msg(nix_c_context * context, const nix_c_context * read_con * @param context optional, the context to store errors in if this function * fails * @param[in] read_context the context to retrieve the error message from - * @param[out] value The allocated area to write the error string to. - * @param[in] n Maximum size of the returned string. + * @param[in] callback Called with the error name. + * @param[in] user_data optional, arbitrary data, passed to the callback when it's called. + * @see nix_observe_string * @return NIX_OK if there were no errors, an error code otherwise. */ -nix_err nix_err_name(nix_c_context * context, const nix_c_context * read_context, char * value, int n); +nix_err nix_err_name(nix_c_context * context, const nix_c_context * read_context, void * callback, void * user_data); /** * @brief Retrieves the most recent error code from a nix_c_context diff --git a/src/libutil/c/nix_api_util_internal.h b/src/libutil/c/nix_api_util_internal.h index 53c260e35..f91d8c118 100644 --- a/src/libutil/c/nix_api_util_internal.h +++ b/src/libutil/c/nix_api_util_internal.h @@ -20,16 +20,16 @@ nix_err nix_context_error(nix_c_context * context); /** * Internal use only. * - * Export a std::string across the C api boundary + * Helper to invoke nix_observe_string * @param context optional, the context to store errors in if this function * fails - * @param str The string to export - * @param value The allocated area to write the string to. - * @param n Maximum size of the returned string. - * @return NIX_OK if there were no errors, NIX_ERR_OVERFLOW if the string length - * exceeds `n`. + * @param str The string to observe + * @param callback Called with the observed string. + * @param user_data optional, arbitrary data, passed to the callback when it's called. + * @return NIX_OK if there were no errors. + * @see nix_observe_string */ -nix_err nix_export_std_string(nix_c_context * context, const std::string_view str, char * dest, unsigned int n); +nix_err call_nix_observe_string(const std::string str, void * callback, void * user_data); #define NIXC_CATCH_ERRS \ catch (...) \ diff --git a/tests/unit/libstore/nix_api_store.cc b/tests/unit/libstore/nix_api_store.cc index dac7fa910..a31d66a4c 100644 --- a/tests/unit/libstore/nix_api_store.cc +++ b/tests/unit/libstore/nix_api_store.cc @@ -7,6 +7,11 @@ namespace nixC { +void observe_string_cb(const char * start, unsigned int n, std::string * user_data) +{ + *user_data = std::string(start); +} + std::string PATH_SUFFIX = "/g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-name"; TEST_F(nix_api_util_context, nix_libstore_init) @@ -17,10 +22,10 @@ TEST_F(nix_api_util_context, nix_libstore_init) TEST_F(nix_api_store_test, nix_store_get_uri) { - char value[256]; - auto ret = nix_store_get_uri(ctx, store, value, 256); + std::string str; + auto ret = nix_store_get_uri(ctx, store, (void *) observe_string_cb, &str); ASSERT_EQ(NIX_OK, ret); - ASSERT_STREQ("local", value); + ASSERT_STREQ("local", str.c_str()); } TEST_F(nix_api_store_test, InvalidPathFails) @@ -50,10 +55,10 @@ TEST_F(nix_api_store_test, DoesNotCrashWhenContextIsNull) TEST_F(nix_api_store_test, get_version) { - char value[256]; - auto ret = nix_store_get_version(ctx, store, value, 256); + std::string str; + auto ret = nix_store_get_version(ctx, store, (void *) observe_string_cb, &str); ASSERT_EQ(NIX_OK, ret); - ASSERT_STREQ(PACKAGE_VERSION, value); + ASSERT_STREQ(PACKAGE_VERSION, str.c_str()); } TEST_F(nix_api_util_context, nix_store_open_dummy) @@ -63,9 +68,9 @@ TEST_F(nix_api_util_context, nix_store_open_dummy) ASSERT_EQ(NIX_OK, ctx->last_err_code); ASSERT_STREQ("dummy", store->ptr->getUri().c_str()); - char value[256]; - nix_store_get_version(ctx, store, value, 256); - ASSERT_STREQ("", value); + std::string str; + nix_store_get_version(ctx, store, (void *) observe_string_cb, &str); + ASSERT_STREQ("", str.c_str()); nix_store_free(store); } diff --git a/tests/unit/libutil-support/tests/nix_api_util.hh b/tests/unit/libutil-support/tests/nix_api_util.hh index 314ec70de..0dfb38f7b 100644 --- a/tests/unit/libutil-support/tests/nix_api_util.hh +++ b/tests/unit/libutil-support/tests/nix_api_util.hh @@ -5,6 +5,7 @@ #include namespace nixC { + class nix_api_util_context : public ::testing::Test { protected: diff --git a/tests/unit/libutil/nix_api_util.cc b/tests/unit/libutil/nix_api_util.cc index a3ec5e0a1..20e46637c 100644 --- a/tests/unit/libutil/nix_api_util.cc +++ b/tests/unit/libutil/nix_api_util.cc @@ -9,6 +9,11 @@ namespace nixC { +void observe_string_cb(const char * start, unsigned int n, std::string * user_data) +{ + *user_data = std::string(start); +} + TEST_F(nix_api_util_context, nix_context_error) { std::string err_msg_ref; @@ -57,13 +62,13 @@ static nix::GlobalConfig::Register rs(&mySettings); TEST_F(nix_api_util_context, nix_setting_get) { ASSERT_EQ(ctx->last_err_code, NIX_OK); - char value[256]; - nix_err result = nix_setting_get(ctx, "invalid-key", value, 256); + std::string setting_value; + nix_err result = nix_setting_get(ctx, "invalid-key", (void *) observe_string_cb, &setting_value); ASSERT_EQ(result, NIX_ERR_KEY); - result = nix_setting_get(ctx, "setting-name", value, 256); + result = nix_setting_get(ctx, "setting-name", (void *) observe_string_cb, &setting_value); ASSERT_EQ(result, NIX_OK); - ASSERT_STREQ("empty", value); + ASSERT_STREQ("empty", setting_value.c_str()); } TEST_F(nix_api_util_context, nix_setting_set) @@ -74,10 +79,10 @@ TEST_F(nix_api_util_context, nix_setting_set) result = nix_setting_set(ctx, "setting-name", "new-value"); ASSERT_EQ(result, NIX_OK); - char value[256]; - result = nix_setting_get(ctx, "setting-name", value, 256); + std::string setting_value; + result = nix_setting_get(ctx, "setting-name", (void *) observe_string_cb, &setting_value); ASSERT_EQ(result, NIX_OK); - ASSERT_STREQ("new-value", value); + ASSERT_STREQ("new-value", setting_value.c_str()); } TEST_F(nix_api_util_context, nix_err_msg) @@ -100,26 +105,26 @@ TEST_F(nix_api_util_context, nix_err_msg) TEST_F(nix_api_util_context, nix_err_info_msg) { + std::string err_info; + // no error - EXPECT_THROW(nix_err_info_msg(NULL, ctx, NULL, 256), nix::Error); + EXPECT_THROW(nix_err_info_msg(NULL, ctx, (void *) observe_string_cb, &err_info), nix::Error); try { throw nix::Error("testing error"); } catch (...) { nix_context_error(ctx); } - char buf[256]; - nix_err_info_msg(nix_c_context_create(), ctx, buf, 256); - ASSERT_EQ(std::string(buf), "testing error"); - - // should overflow - EXPECT_THROW(nix_err_info_msg(NULL, ctx, buf, 1), nix::Error); + nix_err_info_msg(nix_c_context_create(), ctx, (void *) observe_string_cb, &err_info); + ASSERT_STREQ("testing error", err_info.c_str()); } TEST_F(nix_api_util_context, nix_err_name) { + std::string err_name; + // no error - EXPECT_THROW(nix_err_name(NULL, ctx, NULL, 256), nix::Error); + EXPECT_THROW(nix_err_name(NULL, ctx, (void *) observe_string_cb, &err_name), nix::Error); std::string err_msg_ref; try { @@ -127,12 +132,8 @@ TEST_F(nix_api_util_context, nix_err_name) } catch (...) { nix_context_error(ctx); } - char err_name[32]; - nix_err_name(nix_c_context_create(), ctx, err_name, 32); + nix_err_name(nix_c_context_create(), ctx, (void *) observe_string_cb, &err_name); ASSERT_EQ(std::string(err_name), "nix::Error"); - - // overflow - EXPECT_THROW(nix_err_name(NULL, ctx, err_name, 1), nix::Error); } TEST_F(nix_api_util_context, nix_err_code) @@ -141,4 +142,5 @@ TEST_F(nix_api_util_context, nix_err_code) nix_set_err_msg(ctx, NIX_ERR_UNKNOWN, "unknown test error"); ASSERT_EQ(nix_err_code(ctx), NIX_ERR_UNKNOWN); } + }