From 13b3fc90ef2c892569be184cd3d397788f5e79d0 Mon Sep 17 00:00:00 2001 From: Adam Cimarosti Date: Mon, 29 Jan 2024 17:27:29 +0000 Subject: [PATCH] C iterator API test --- questdb-confstr-ffi/cpp_test/test.cpp | 11 +++ .../include/questdb/conf_str.h | 15 ++++ .../include/questdb/conf_str.hpp | 82 +++++++++++++++++++ questdb-confstr-ffi/src/lib.rs | 52 ++++++++++++ 4 files changed, 160 insertions(+) diff --git a/questdb-confstr-ffi/cpp_test/test.cpp b/questdb-confstr-ffi/cpp_test/test.cpp index 49539b4..decca7a 100644 --- a/questdb-confstr-ffi/cpp_test/test.cpp +++ b/questdb-confstr-ffi/cpp_test/test.cpp @@ -23,6 +23,7 @@ ******************************************************************************/ #include +#include #include @@ -56,4 +57,14 @@ TEST_CASE("parse error") } } +TEST_CASE("iter params") { + const auto c1 = conf_str::parse("http::host=localhost;port=9000;"); + std::unordered_map params; + for (auto it = c1.begin(); it != c1.end(); ++it) { + params.emplace(it.key(), it.value()); + } + CHECK(params.size() == 2); + CHECK(params["host"] == "localhost"); + CHECK(params["port"] == "9000"); +} diff --git a/questdb-confstr-ffi/include/questdb/conf_str.h b/questdb-confstr-ffi/include/questdb/conf_str.h index 68c8faa..ad83afd 100644 --- a/questdb-confstr-ffi/include/questdb/conf_str.h +++ b/questdb-confstr-ffi/include/questdb/conf_str.h @@ -1,6 +1,7 @@ #pragma once #include +#include #if defined(__cplusplus) extern "C" { @@ -18,6 +19,8 @@ typedef struct questdb_conf_str_parse_err questdb_conf_str_parse_err; void questdb_conf_str_parse_err_free(questdb_conf_str_parse_err* err); +typedef struct questdb_conf_str_iter questdb_conf_str_iter; + questdb_conf_str* questdb_conf_str_parse( const char* str, size_t len, @@ -33,6 +36,18 @@ const char* questdb_conf_str_get( size_t key_len, size_t* val_len_out); +questdb_conf_str_iter* questdb_conf_str_iter_pairs( + const questdb_conf_str* conf_str); + +bool questdb_conf_str_iter_next( + questdb_conf_str_iter* iter, + const char** key_out, + size_t* key_len_out, + const char** val_out, + size_t* val_len_out); + +void questdb_conf_str_iter_free(questdb_conf_str_iter* iter); + void questdb_conf_str_free(questdb_conf_str* str); #if defined(__cplusplus) diff --git a/questdb-confstr-ffi/include/questdb/conf_str.hpp b/questdb-confstr-ffi/include/questdb/conf_str.hpp index e974a06..fc3bca1 100644 --- a/questdb-confstr-ffi/include/questdb/conf_str.hpp +++ b/questdb-confstr-ffi/include/questdb/conf_str.hpp @@ -30,6 +30,77 @@ class parse_err questdb_conf_str_parse_err* _impl; }; +class pair_iter +{ +public: + pair_iter(const pair_iter&) = delete; + + bool operator==(const pair_iter& other) const noexcept + { + return _impl == other._impl; + } + + bool operator!=(const pair_iter& other) const noexcept + { + return !(*this == other); + } + + void operator++() noexcept + { + const char* key = nullptr; + size_t key_len = 0; + const char* val = nullptr; + size_t val_len = 0; + const bool has = ::questdb_conf_str_iter_next( + _impl, + &key, + &key_len, + &val, + &val_len); + if (has) + { + _key = { key, key_len }; + _val = { val, val_len }; + } + else + { + _key = {}; + _val = {}; + ::questdb_conf_str_iter_free(_impl); + _impl = nullptr; + } + } + + ~pair_iter() noexcept + { + if (_impl != nullptr) + { + ::questdb_conf_str_iter_free(_impl); + } + } + + std::string_view key() const noexcept + { + return _key; + } + + std::string_view value() const noexcept + { + return _val; + } +private: + friend class conf_str; + pair_iter(::questdb_conf_str_iter* impl) : _impl(impl) + { + if (_impl != nullptr) + { + ++(*this); + } + } + ::questdb_conf_str_iter* _impl; + std::string_view _key; + std::string_view _val; +}; class conf_str { @@ -66,6 +137,17 @@ class conf_str return {}; } + pair_iter begin() const noexcept + { + auto iter = ::questdb_conf_str_iter_pairs(_impl); + return { iter }; + } + + pair_iter end() const noexcept + { + return pair_iter{ nullptr }; + } + ~conf_str() noexcept { ::questdb_conf_str_free(_impl); diff --git a/questdb-confstr-ffi/src/lib.rs b/questdb-confstr-ffi/src/lib.rs index fe7ef8c..e9568a7 100644 --- a/questdb-confstr-ffi/src/lib.rs +++ b/questdb-confstr-ffi/src/lib.rs @@ -26,6 +26,7 @@ #![allow(clippy::missing_safety_doc)] use questdb_confstr::{parse_conf_str, ConfStr}; +use std::collections::hash_map; use std::os::raw::c_char; use std::ptr; use std::slice; @@ -128,6 +129,57 @@ pub unsafe extern "C" fn questdb_conf_str_get( } } +#[repr(C)] +pub struct questdb_conf_str_iter { + inner: hash_map::Iter<'static, String, String>, +} + +#[no_mangle] +pub unsafe extern "C" fn questdb_conf_str_iter_pairs( + conf_str: *const questdb_conf_str, +) -> *mut questdb_conf_str_iter { + if conf_str.is_null() { + return ptr::null_mut(); + } + let conf_str = &(*conf_str).inner; + let iter = questdb_conf_str_iter { + inner: conf_str.params().iter(), + }; + Box::into_raw(Box::new(iter)) +} + +#[no_mangle] +pub unsafe extern "C" fn questdb_conf_str_iter_next( + iter: *mut questdb_conf_str_iter, + key_out: *mut *const c_char, + key_len_out: *mut usize, + val_out: *mut *const c_char, + val_len_out: *mut usize, +) -> bool { + let iter = &mut *iter; + match iter.inner.next() { + Some((key, val)) => { + let key_str = key.as_ptr() as *const c_char; + let val_str = val.as_ptr() as *const c_char; + unsafe { + *key_out = key_str; + *key_len_out = key.len(); + *val_out = val_str; + *val_len_out = val.len(); + } + true + } + None => false, + } +} + +#[no_mangle] +pub unsafe extern "C" fn questdb_conf_str_iter_free(iter: *mut questdb_conf_str_iter) { + if !iter.is_null() { + drop(Box::from_raw(iter)); + } +} + #[no_mangle] pub unsafe extern "C" fn questdb_conf_str_free(conf_str: *mut questdb_conf_str) { if !conf_str.is_null() {