Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions bindings/c/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ set(SVS_C_API_SOURCES
src/index_vamana.hpp
src/index_builder.hpp
src/leanvec_training_data.hpp
src/logger.hpp
src/storage.hpp
src/stream.hpp
src/threadpool.hpp
Expand Down
157 changes: 157 additions & 0 deletions bindings/c/include/svs/c/svs_c.h
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,26 @@ enum svs_error_code {
SVS_ERROR_UNKNOWN = 1000
};

/// @brief Severity of a log message.
enum svs_log_level {
SVS_LOG_LEVEL_TRACE = 0,
SVS_LOG_LEVEL_DEBUG = 1,
SVS_LOG_LEVEL_INFO = 2,
SVS_LOG_LEVEL_WARN = 3,
SVS_LOG_LEVEL_ERROR = 4,
SVS_LOG_LEVEL_CRITICAL = 5,
SVS_LOG_LEVEL_OFF = 6
};

/// @brief Output of a logger created with svs_logger_create().
enum svs_logging_kind {
SVS_LOGGING_KIND_NONE = 0,
SVS_LOGGING_KIND_STDOUT = 1,
SVS_LOGGING_KIND_STDERR = 2,
SVS_LOGGING_KIND_FILE_APPEND = 3,
SVS_LOGGING_KIND_FILE_TRUNCATE = 4
};

typedef struct svs_error_desc* svs_error_h;

/// @brief Distance metric used to compare vectors.
Expand Down Expand Up @@ -124,6 +144,45 @@ enum svs_threadpool_kind {
SVS_THREADPOOL_KIND_CUSTOM = 3
};

/// @brief Operations table for a custom logging interface
/// @remarks The user must ensure that the log function is thread-safe (it may be called
/// from SVS worker threads and must not throw or longjmp) and that the operations table
/// and @p self remain valid while any logger, index or default logger using them exists.
/// @var svs_logging_interface_ops::version
/// Version of the logging interface.
/// @var svs_logging_interface_ops::struct_size
/// Size of the structure, used for versioning and compatibility checks.
/// @var svs_logging_interface_ops::log
/// Function pointer to log a message.
/// @param self Pointer to the logging interface instance.
/// @param level Logging level of the message.
/// @param message Null-terminated bare message text, valid only for the duration of the
/// call.
struct svs_logging_interface_ops {
uint32_t version;
size_t struct_size;
void (*log)(void* self, enum svs_log_level level, const char* message);
};

/// @brief Macro to create a user-defined logging interface operations structure
/// @param log_func Function pointer to log a message
#define SVS_INIT_LOGGING_OPS(log_func) \
{ \
.version = SVS_C_API_VERSION, \
.struct_size = sizeof(struct svs_logging_interface_ops), .log = &log_func \
}

/// @brief Structure representing a custom logging interface
/// @var svs_logging_interface::ops
/// Function pointers for the logging operations.
/// @var svs_logging_interface::self
/// Pointer to the user-defined logger instance. This pointer is passed to the
/// function pointers in @p ops when they are called.
struct svs_logging_interface {
const struct svs_logging_interface_ops* ops;
void* self;
};

/// @brief Operations table for a custom thread pool interface
/// @remarks The user must ensure that the thread pool implementation is thread-safe and
/// that the provided function pointers remain valid for the lifetime of the thread pool
Expand Down Expand Up @@ -536,16 +595,22 @@ typedef struct svs_algorithm* svs_algorithm_h;
typedef struct svs_storage* svs_storage_h;
typedef struct svs_search_params* svs_search_params_h;
typedef struct svs_leanvec_training_data* svs_leanvec_training_data_h;
typedef struct svs_logger* svs_logger_h;

// Fully defined types; "_t" suffix indicates a fully defined struct
typedef enum svs_error_code svs_error_code_t;
typedef enum svs_logging_kind svs_logging_kind_t;
typedef enum svs_log_level svs_log_level_t;
typedef enum svs_distance_metric svs_distance_metric_t;
typedef enum svs_algorithm_type svs_algorithm_type_t;
typedef enum svs_data_type svs_data_type_t;
typedef enum svs_storage_kind svs_storage_kind_t;
typedef enum svs_threadpool_kind svs_threadpool_kind_t;
typedef enum svs_allocator_kind svs_allocator_kind_t;

typedef struct svs_logging_interface_ops svs_logging_ops_t;
typedef struct svs_logging_interface svs_logging_t;
typedef struct svs_logging_interface* svs_logging_i;
typedef struct svs_threadpool_interface_ops svs_threadpool_ops_t;
typedef struct svs_threadpool_interface svs_threadpool_t;
typedef struct svs_threadpool_interface* svs_threadpool_i;
Expand Down Expand Up @@ -609,6 +674,86 @@ SVS_API const char* svs_error_get_message(svs_error_h err);
/// @param err The error handle to free
SVS_API void svs_error_free(svs_error_h err);

/// @brief Create a logger writing to a built-in output
/// @param kind The output kind of the logger
/// @param path The file to write to for the SVS_LOGGING_KIND_FILE_* kinds; ignored
/// otherwise
/// @param out_err An optional error handle to capture errors
/// @return A handle to the created logger or NULL if creation failed
/// @remarks The output of a logger is fixed at creation; a new logger has level
/// SVS_LOG_LEVEL_WARN and pattern "%v".
SVS_API svs_logger_h svs_logger_create(
svs_logging_kind_t kind, const char* path, svs_error_h out_err /*=NULL*/
);

/// @brief Create a logger forwarding every message to a user callback
/// @param user_logger The custom logging interface, initialized with SVS_INIT_LOGGING_OPS
/// @param out_err An optional error handle to capture errors
/// @return A handle to the created logger or NULL if creation failed
/// @remarks The callback may be called from SVS worker threads and must be thread-safe and
/// must not throw or longjmp. It receives the bare message; the logger pattern does not
/// apply to it.
SVS_API svs_logger_h
svs_logger_create_custom(svs_logging_i user_logger, svs_error_h out_err /*=NULL*/);

/// @brief Free a logger handle
/// @param logger The logger handle to free
/// @remarks Indexes and the SVS default logger using this logger keep it alive after the
/// handle is freed.
SVS_API void svs_logger_free(svs_logger_h logger);

/// @brief Set the logging level for a logger
/// @param logger The logger handle
/// @param level The logging level to set
/// @param out_err An optional error handle to capture errors
/// @return true on success, false on failure
/// @remarks Unknown level values fail with SVS_ERROR_INVALID_ARGUMENT.
SVS_API bool svs_logger_set_level(
svs_logger_h logger, svs_log_level_t level, svs_error_h out_err /*=NULL*/
);

/// @brief Get the logging level for a logger
/// @param logger The logger handle
/// @param out_level Pointer to store the retrieved logging level
/// @param out_err An optional error handle to capture errors
/// @return true on success, false on failure
SVS_API bool svs_logger_get_level(
svs_logger_h logger, svs_log_level_t* out_level, svs_error_h out_err /*=NULL*/
);

/// @brief Set format pattern for a logger
/// @param logger The logger handle
/// @param pattern The format pattern to set, in spdlog syntax (e.g. "[index A] %v")

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does it make sense to add a link to the spdlog documentation?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes — added a link to spdlog's pattern syntax.

/// Pattern syntax: https://github.com/gabime/spdlog/wiki/Custom-formatting
/// @param out_err An optional error handle to capture errors
/// @return true on success, false on failure
/// @remarks The pattern applies to the stdout, stderr and file outputs; custom callbacks
/// receive the bare message.
SVS_API bool svs_logger_set_pattern(
svs_logger_h logger, const char* pattern, svs_error_h out_err /*=NULL*/
);

/// @brief Get the format pattern for a logger
/// @param logger The logger handle
/// @param out_pattern Pointer to store the retrieved format pattern (default "%v")
/// @param out_err An optional error handle to capture errors
/// @return true on success, false on failure
/// @remarks The returned pointer is valid until the next svs_logger_set_pattern() call on
/// the handle or until svs_logger_free().
SVS_API bool svs_logger_get_pattern(
svs_logger_h logger, const char** out_pattern, svs_error_h out_err /*=NULL*/
);

/// @brief Set default logger for SVS library
/// @param logger The logger handle to set as default; NULL restores the SVS built-in
/// default
/// @param out_err An optional error handle to capture errors
/// @return true on success, false on failure
/// @remarks Indexes keep the default logger current when they are built or loaded.
SVS_API bool svs_set_default_logger(
svs_logger_h logger, svs_error_h out_err /*=NULL*/
);

/// @brief Create a Vamana algorithm configuration
/// @param graph_degree The graph degree parameter
/// @param build_window_size The build window size parameter
Expand Down Expand Up @@ -841,6 +986,18 @@ SVS_API svs_index_builder_h svs_index_builder_create(
/// @param builder The index builder handle to free
SVS_API void svs_index_builder_free(svs_index_builder_h builder);

/// @brief Set the logger for the index builder
/// @param builder The index builder handle
/// @param logger The logger handle for indexes built, loaded or converted with this
/// builder; NULL uses the global default logger
/// @param out_err An optional error handle to capture errors
/// @return true on success, false on failure
/// @remarks Indexes keep the logger they were created with; the handle may be freed after
/// this call. Stream loads ignore it and use the global default logger.
SVS_API bool svs_index_builder_set_logger(
svs_index_builder_h builder, svs_logger_h logger, svs_error_h out_err /*=NULL*/
);

/// @brief Set the storage configuration for the index builder
/// @param builder The index builder handle
/// @param storage The storage configuration handle
Expand Down
51 changes: 35 additions & 16 deletions bindings/c/src/dispatcher_dynamic_vamana.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@

#include <svs/concepts/data.h>
#include <svs/core/distance.h>
#include <svs/core/logging.h>
#include <svs/core/query_result.h>
#include <svs/index/vamana/build_params.h>
#include <svs/index/vamana/dynamic_index.h>
Expand All @@ -48,7 +49,8 @@ svs::DynamicVamana build_dynamic_vamana_index(
Distance D,
svs::threads::ThreadPoolHandle pool,
const AllocatorBuilder& allocator_builder,
size_t blocksize_bytes
size_t blocksize_bytes,
svs::logging::logger_ptr logger
) {
svs::data::BlockingParameters block_params;
if (blocksize_bytes != 0) {
Expand All @@ -69,7 +71,8 @@ svs::DynamicVamana build_dynamic_vamana_index(
std::move(src_data.second),
std::move(D),
std::move(pool),
graph_allocator
graph_allocator,
std::move(logger)
);
}

Expand All @@ -81,7 +84,8 @@ svs::DynamicVamana load_dynamic_vamana_index(
Distance D,
svs::threads::ThreadPoolHandle pool,
const AllocatorBuilder& allocator_builder,
size_t blocksize_bytes
size_t blocksize_bytes,
svs::logging::logger_ptr logger
) {
svs::data::BlockingParameters block_params;
if (blocksize_bytes != 0) {
Expand All @@ -101,7 +105,9 @@ svs::DynamicVamana load_dynamic_vamana_index(
svs::GraphLoader{directory / "graph", graph_allocator},
std::move(data),
std::move(D),
std::move(pool)
std::move(pool),
/*debug_load_from_static=*/false,
std::move(logger)
);
}

Expand All @@ -113,7 +119,8 @@ svs::DynamicVamana load_stream_dynamic_vamana_index(
Distance distance,
svs::threads::ThreadPoolHandle pool,
const AllocatorBuilder& allocator_builder,
size_t blocksize_bytes
size_t blocksize_bytes,
svs::logging::logger_ptr SVS_UNUSED(logger)
) {
svs::data::BlockingParameters block_params;
if (blocksize_bytes != 0) {
Expand Down Expand Up @@ -176,7 +183,8 @@ using BuildDynamicIndexDispatcher = svs::lib::Dispatcher<
svs::DistanceType,
svs::threads::ThreadPoolHandle,
const AllocatorBuilder&,
size_t>;
size_t,
svs::logging::logger_ptr>;

const BuildDynamicIndexDispatcher& build_dynamic_vamana_index_dispatcher() {
static BuildDynamicIndexDispatcher dispatcher = [] {
Expand All @@ -196,7 +204,8 @@ using CopyDynamicIndexDispatcher = svs::lib::Dispatcher<
svs::DistanceType,
svs::threads::ThreadPoolHandle,
const AllocatorBuilder&,
size_t>;
size_t,
svs::logging::logger_ptr>;

template <typename SrcDataBuilder, typename DstDataBuilder, typename Distance>
svs::DynamicVamana copy_dynamic_vamana_index(
Expand All @@ -207,7 +216,8 @@ svs::DynamicVamana copy_dynamic_vamana_index(
Distance distance,
svs::threads::ThreadPoolHandle pool,
const AllocatorBuilder& allocator_builder,
size_t blocksize_bytes
size_t blocksize_bytes,
svs::logging::logger_ptr logger
) {
auto config = src_index.parameters();

Expand Down Expand Up @@ -273,7 +283,9 @@ svs::DynamicVamana copy_dynamic_vamana_index(
std::move(graph),
std::move(data),
distance,
std::move(pool)
std::move(pool),
/*debug_load_from_static=*/false,
std::move(logger)
);
}

Expand Down Expand Up @@ -342,7 +354,8 @@ svs::DynamicVamana dispatch_dynamic_vamana_index_build(
svs::DistanceType distance_type,
svs::threads::ThreadPoolHandle pool,
const AllocatorBuilder& allocator_builder,
size_t blocksize_bytes
size_t blocksize_bytes,
svs::logging::logger_ptr logger
) {
return build_dynamic_vamana_index_dispatcher().invoke(
build_params,
Expand All @@ -351,7 +364,8 @@ svs::DynamicVamana dispatch_dynamic_vamana_index_build(
distance_type,
std::move(pool),
allocator_builder,
blocksize_bytes
blocksize_bytes,
std::move(logger)
);
}

Expand All @@ -362,7 +376,8 @@ svs::DynamicVamana dispatch_dynamic_vamana_index_load(
svs::DistanceType distance_type,
svs::threads::ThreadPoolHandle pool,
const AllocatorBuilder& allocator_builder,
size_t blocksize_bytes
size_t blocksize_bytes,
svs::logging::logger_ptr logger
) {
return build_dynamic_vamana_index_dispatcher().invoke(
build_params,
Expand All @@ -371,7 +386,8 @@ svs::DynamicVamana dispatch_dynamic_vamana_index_load(
distance_type,
std::move(pool),
allocator_builder,
blocksize_bytes
blocksize_bytes,
std::move(logger)
);
}

Expand All @@ -391,7 +407,8 @@ svs::DynamicVamana dispatch_dynamic_vamana_index_load_stream(
distance_type,
std::move(pool),
allocator_builder,
blocksize_bytes
blocksize_bytes,
nullptr
);
}

Expand All @@ -403,7 +420,8 @@ svs::DynamicVamana dispatch_dynamic_vamana_index_copy(
svs::DistanceType distance_type,
svs::threads::ThreadPoolHandle pool,
const AllocatorBuilder& allocator_builder,
size_t blocksize_bytes
size_t blocksize_bytes,
svs::logging::logger_ptr logger
) {
return copy_dynamic_index_dispatcher().invoke(
build_params,
Expand All @@ -413,7 +431,8 @@ svs::DynamicVamana dispatch_dynamic_vamana_index_copy(
distance_type,
std::move(pool),
allocator_builder,
blocksize_bytes
blocksize_bytes,
std::move(logger)
);
}

Expand Down
Loading
Loading